Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>;
}
private boolean isCallToScopeMethod(Node n) {
return n.isCall() &&
SCOPING_METHOD_NAME.equals(n.getFirstChild().getQualifiedName());
}
@Override
public void enterScope(NodeTraversal t) {
Node n = t.getCurrentNode().getParent();
if (n != null && isCallToScopeMethod(n)) {
transformation = transformationHandler.logAliasTransformation(
n.getSourceFileName(), getSourceRegion(n));
findAliases(t);
}
}
@Override
public void exitScope(NodeTraversal t) {
if (t.getScopeDepth() == 2) {
aliases.clear();
transformation = null;
}
}
@Override
public final boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
if (n.isFunction() && t.inGlobalScope()) {
// Do not traverse in to functions except for goog.scope functions.
if (parent == null || !isCallToScopeMethod(parent)) {
return false;
}
}
return true;
}
private SourcePosition<AliasTransformation> getSourceRegion(Node n) {
Node testNode = n;
Node next = null;
for (; next != null || testNode.isScript();) {
next = testNode.getNext();
testNode = testNode.getParent();
}
int endLine = next == null ? Integer.MAX_VALUE : next.getLineno();
// child is the "goog.scope" and the second should be the parameter.
report(t, n, GOOG_SCOPE_HAS_BAD_PARAMETERS);
} else {
Node anonymousFnNode = n.getChildAtIndex(1);
if (!anonymousFnNode.isFunction() ||
NodeUtil.getFunctionName(anonymousFnNode) != null ||
NodeUtil.getFunctionParameters(anonymousFnNode).hasChildren()) {
report(t, anonymousFnNode, GOOG_SCOPE_HAS_BAD_PARAMETERS);
} else {
scopeCalls.add(n);
}
}
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (isCallToScopeMethod(n)) {
validateScopeCall(t, n, n.getParent());
}
if (t.getScopeDepth() < 2) {
return;
}
int type = n.getType();
Var aliasVar = null;
if (type == Token.NAME) {
String name = n.
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>getString();
Var lexicalVar = t.getScope().getVar(n.getString());
if (lexicalVar != null && lexicalVar == aliases.get(name)) {
aliasVar = lexicalVar;
}
}
// Validate the top-level of the goog.scope block.
if (t.getScopeDepth() == 2) {
if (aliasVar != null && NodeUtil.isLValue(n)) {
if (aliasVar.getNode() == n) {
aliasDefinitionsInOrder.add(n);
// Return early, to ensure that we don't record a definition
// twice.
return;
} else {
report(t, n, GOOG_SCOPE_ALIAS_REDEFINED, n.getString());
}
}
if (type == Token.RETURN) {
report(t, n, GOOG_SCOPE_USES_RETURN);
} else if (type == Token.THIS) {
report(t, n, GOOG_SCOPE_REFERENCES_THIS);
} else if (type == Token.THROW) {
report(t, n, GOOG_SCOPE_USES_THROW);
}
}
// Validate all descendent scopes of the goog.scope block.
if (t.getScopeDepth() >= 2) {
// Check if this name points to an alias.
if (aliasVar != null) {
// Note, to support the transitive case, it's important we don't
// clone aliasedNode here. For example,
// var g = goog; var d = g.dom; d.createElement('DIV');
// The node in aliasedNode (which is "g") will be replaced in the
// changes pass above with "goog". If we cloned here, we'd end up
// with <code>g.dom.createElement('DIV')</code>.
Node aliasedNode = aliasVar.getInitialValue();
aliasUsages.add(new AliasedNode(n, aliasedNode));
}
JSDocInfo info = n.getJSDocInfo();
if (info != null) {
for (Node node : info.getTypeNodes()) {
fixTypeNode(node);
}
}
// TODO(robbyw): Error for goog.scope not at root.
}
}
private void fixTypeNode(Node typeNode) {
if (typeNode.isString()) {
String name = typeNode.getString();
int endIndex = name.indexOf('.');
if (endIndex
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>/*
* Copyright 2004 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.base.Preconditions;
import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import java.util.*;
/**
* Verifies that constants are only assigned a value once.
* e.g. var XX = 5;
* XX = 3; // error!
* XX++; // error!
*
*/
class ConstCheck extends AbstractPostOrderCallback
implements CompilerPass {
static final DiagnosticType CONST_REASSIGNED_VALUE_ERROR =
DiagnosticType.error(
"JSC_CONSTANT_REASSIGNED_VALUE_ERROR",
"constant {0} assigned a value more than once");
private final AbstractCompiler compiler;
private final Set<Scope.Var> initializedConstants;
/**
* Creates an instance.
*/
public ConstCheck(AbstractCompiler compiler) {
this.compiler = compiler;
this.initializedConstants = new HashSet<Scope.Var>();
}
@Override
public void process(Node externs, Node root) {
Preconditions.checkState(compiler.getLifeCycleStage().isNormalized());
NodeTraversal.traverse(compiler, root, this);
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.NAME:
if (parent != null &&
parent.isVar() &&
n.hasChildren()) {
String name = n.getString();
Scope.Var var = t.getScope().getVar(name);
if (isConstant(var)) {
if (initializedConstants.contains(var)) {
reportError(t, n, name);
} else {
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> initializedConstants.add(var);
}
}
}
break;
case Token.ASSIGN:
case Token.ASSIGN_BITOR:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_BITAND:
case Token.ASSIGN_LSH:
case Token.ASSIGN_RSH:
case Token.ASSIGN_URSH:
case Token.ASSIGN_ADD:
case Token.ASSIGN_SUB:
case Token.ASSIGN_MUL:
case Token.ASSIGN_DIV:
case Token.ASSIGN_MOD: {
Node lhs = n.getFirstChild();
if (lhs.isName()) {
String name = lhs.getString();
Scope.Var var = t.getScope().getVar(name);
if (isConstant(var)) {
if (initializedConstants.contains(var)) {
reportError(t, n, name);
} else {
initializedConstants.add(var);
}
}
}
break;
}
case Token.INC:
case Token.DEC: {
Node lhs = n.getFirstChild();
if (lhs.isName()) {
String name = lhs.getString();
Scope.Var var = t.getScope().getVar(name);
if (isConstant(var)) {
reportError(t, n, name);
}
}
break;
}
}
}
/**
* Gets whether a variable is a constant initialized to a literal value at
* the point where it is declared.
*/
private boolean isConstant(Scope.Var var) {
return var != null && var.isConst();
}
/**
* Reports a reassigned constant error.
*/
void reportError(NodeTraversal t, Node n, String name) {
compiler.report(t.makeError(n, CONST_REASSIGNED_VALUE_ERROR, name));
}
}
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>, this);
// Code with hidden side-effect code is common, for example
// accessing "el.offsetWidth" forces a reflow in browsers, to allow this
// will still allowing local dead code removal in general,
// protect the "side-effect free" code in the source.
//
if (protectSideEffectFreeCode) {
protectSideEffects();
}
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
NodeTraversal.traverse(compiler, scriptRoot, this);
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
// VOID nodes appear when there are extra semicolons at the BLOCK level.
// I've been unable to think of any cases where this indicates a bug,
// and apparently some people like keeping these semicolons around,
// so we'll allow it.
if (n.isEmpty() ||
n.isComma()) {
return;
}
if (parent == null) {
return;
}
int pt = parent.getType();
if (pt == Token.COMMA) {
Node gramps = parent.getParent();
if (gramps.isCall() &&
parent == gramps.getFirstChild()) {
// Semantically, a direct call to eval is different from an indirect
// call to an eval. See ECMA-262 S15.1.2.1. So it's OK for the first
// expression to a comma to be a no-op if it's used to indirect
// an eval.
if (n == parent.getFirstChild() &&
parent.getChildCount() == 2 &&
n.getNext().isName() &&
"eval".equals(n.getNext().getString())) {
return;
}
}
if (n == parent.getLastChild()) {
for (Node an : parent.getAncestors()) {
int ancestorType = an.getType();
if (ancestorType == Token.COMMA)
continue;
if (ancestorType != Token.EXPR_RESULT &&
ancestorType != Token.BLOCK)
return;
else
break;
}
}
} else if (pt != Token.EXPR_RESULT && pt != Token.BLOCK) {
if (pt == Token.FOR && parent.getChildCount() == 4 &&
(n == parent.getFirstChild() ||
n == parent.getFirstChild().getNext().getNext())) {
// Fall through and look for warnings for the 1st and 3rd child
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> // of a for.
} else {
return; // it might be OK to not have a side-effect
}
}
boolean isSimpleOp = NodeUtil.isSimpleOperatorType(n.getType());
if (isSimpleOp ||
!NodeUtil.mayHaveSideEffects(n, t.getCompiler())) {
if (n.isQualifiedName() && n.getJSDocInfo() != null) {
// This no-op statement was there so that JSDoc information could
// be attached to the name. This check should not complain about it.
return;
} else if (n.isExprResult()) {
// we already reported the problem when we visited the child.
return;
}
String msg = "This code lacks side-effects. Is there a bug?";
if (n.isString()) {
msg = "Is there a missing '+' on the previous line?";
} else if (isSimpleOp) {
msg = "The result of the '" + Token.name(n.getType()).toLowerCase() +
"' operator is not being used.";
}
t.getCompiler().report(
t.makeError(n, level, USELESS_CODE_ERROR, msg));
// TODO(johnlenz): determine if it is necessary to
// try to protect side-effect free statements as well.
if (!NodeUtil.isStatement(n)) {
problemNodes.add(n);
}
}
}
/**
* Protect side-effect free nodes by making them parameters
* to a extern function call. This call will be removed
* after all the optimizations passes have run.
*/
private void protectSideEffects() {
if (!problemNodes.isEmpty()) {
addExtern();
for (Node n : problemNodes) {
Node name = IR.name(PROTECTOR_FN).srcref(n);
name.putBooleanProp(Node.IS_CONSTANT_NAME, true);
Node replacement = IR.call(name).srcref(n);
replacement.putBooleanProp(Node.FREE_CALL, true);
n.getParent().replaceChild(n, replacement);
replacement.addChildToBack(n);
}
compiler.reportCodeChange();
}
}
private void addExtern() {
Node name = IR.name(PROTECTOR_FN);
name.putBooleanProp(Node.IS_CONSTANT_NAME, true);
Node var = IR.var(name);
// Add "@noalias" so we
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> null) {
NodeTraversal.traverse(
compiler, root, new PrepareAnnotations(compiler));
}
}
}
/**
* Covert EXPR_VOID to EXPR_RESULT to simplify the rest of the code.
*/
private void normalizeNodeTypes(Node n) {
normalizeBlocks(n);
for (Node child = n.getFirstChild();
child != null; child = child.getNext()) {
// This pass is run during the CompilerTestCase validation, so this
// parent pointer check serves as a more general check.
Preconditions.checkState(child.getParent() == n);
normalizeNodeTypes(child);
}
}
/**
* Add blocks to IF, WHILE, DO, etc.
*/
private void normalizeBlocks(Node n) {
if (NodeUtil.isControlStructure(n)
&& !n.isLabel()
&& !n.isSwitch()) {
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
if (NodeUtil.isControlStructureCodeBlock(n,c) &&
!c.isBlock()) {
Node newBlock = IR.block().srcref(n);
n.replaceChild(c, newBlock);
if (!c.isEmpty()) {
newBlock.addChildrenToFront(c);
} else {
newBlock.setWasEmptyNode(true);
}
c = newBlock;
reportChange();
}
}
}
}
/**
* Normalize where annotations appear on the AST. Copies
* around existing JSDoc annotations as well as internal annotations.
*/
static class PrepareAnnotations
implements NodeTraversal.Callback {
private final CodingConvention convention;
PrepareAnnotations(AbstractCompiler compiler) {
this.convention = compiler.getCodingConvention();
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
if (n.isObjectLit()) {
normalizeObjectLiteralAnnotations(n);
}
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.CALL:
annotateCalls(n);
break;
case Token.FUNCTION:
annotateFunctions(n, parent);
annotateDispatchers(n, parent);
break;
}
}
private void normalizeObjectLiteralAnnotations(Node objlit) {
Preconditions.checkState(objlit.isObjectLit());
for (Node key = objlit.getFirstChild();
key
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>ging function for non-equality between types.
*/
private static final Function<TypePair, TypePair> NE =
new Function<TypePair, TypePair>() {
@Override
public TypePair apply(TypePair p) {
if (p.typeA == null || p.typeB == null) {
return null;
}
return p.typeA.getTypesUnderInequality(p.typeB);
}
};
/**
* Merging function for strict equality between types.
*/
private static final
Function<TypePair, TypePair> SHEQ =
new Function<TypePair, TypePair>() {
@Override
public TypePair apply(TypePair p) {
if (p.typeA == null || p.typeB == null) {
return null;
}
return p.typeA.getTypesUnderShallowEquality(p.typeB);
}
};
/**
* Merging function for strict non-equality between types.
*/
private static final
Function<TypePair, TypePair> SHNE =
new Function<TypePair, TypePair>() {
@Override
public TypePair apply(TypePair p) {
if (p.typeA == null || p.typeB == null) {
return null;
}
return p.typeA.getTypesUnderShallowInequality(p.typeB);
}
};
/**
* Merging function for inequality comparisons between types.
*/
private final
Function<TypePair, TypePair> INEQ =
new Function<TypePair, TypePair>() {
@Override
public TypePair apply(TypePair p) {
return new TypePair(
getRestrictedWithoutUndefined(p.typeA),
getRestrictedWithoutUndefined(p.typeB));
}
};
/**
* Creates a semantic reverse abstract interpreter.
*/
public SemanticReverseAbstractInterpreter(CodingConvention convention,
JSTypeRegistry typeRegistry) {
super(convention, typeRegistry);
}
@Override
public FlowScope getPreciserScopeKnowingConditionOutcome(Node condition,
FlowScope blindScope, boolean outcome) {
// Check for the typeof operator.
int operatorToken = condition.getType();
switch (operatorToken) {
case Token.EQ:
case Token.NE:
case Token.SHEQ:
case Token.SHNE:
case Token.CASE:
Node left;
Node right;
if (operatorToken == Token.CASE) {
left = condition.getParent().getFirstChild(); // the
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> switch condition
right = condition.getFirstChild();
} else {
left = condition.getFirstChild();
right = condition.getLastChild();
}
Node typeOfNode = null;
Node stringNode = null;
if (left.isTypeOf() && right.isString()) {
typeOfNode = left;
stringNode = right;
} else if (right.isTypeOf() &&
left.isString()) {
typeOfNode = right;
stringNode = left;
}
if (typeOfNode != null && stringNode != null) {
Node operandNode = typeOfNode.getFirstChild();
JSType operandType = getTypeIfRefinable(operandNode, blindScope);
if (operandType != null) {
boolean resultEqualsValue = operatorToken == Token.EQ ||
operatorToken == Token.SHEQ || operatorToken == Token.CASE;
if (!outcome) {
resultEqualsValue = !resultEqualsValue;
}
return caseTypeOf(operandNode, operandType, stringNode.getString(),
resultEqualsValue, blindScope);
}
}
}
switch (operatorToken) {
case Token.AND:
if (outcome) {
return caseAndOrNotShortCircuiting(condition.getFirstChild(),
condition.getLastChild(), blindScope, true);
} else {
return caseAndOrMaybeShortCircuiting(condition.getFirstChild(),
condition.getLastChild(), blindScope, true);
}
case Token.OR:
if (!outcome) {
return caseAndOrNotShortCircuiting(condition.getFirstChild(),
condition.getLastChild(), blindScope, false);
} else {
return caseAndOrMaybeShortCircuiting(condition.getFirstChild(),
condition.getLastChild(), blindScope, false);
}
case Token.EQ:
if (outcome) {
return caseEquality(condition, blindScope, EQ);
} else {
return caseEquality(condition, blindScope, NE);
}
case Token.NE:
if (outcome) {
return caseEquality(condition, blindScope, NE);
} else {
return caseEquality(condition, blindScope, EQ);
}
case Token.SHEQ:
if (outcome) {
return caseEquality(condition, blindScope, SHEQ);
} else {
return caseEquality(condition, blindScope, SHNE);
}
case Token.SHNE:
if (outcome) {
return caseEquality(condition, blindScope, SHNE);
} else {
return caseEquality(condition, blindScope, S
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>HEQ);
}
case Token.NAME:
case Token.GETPROP:
return caseNameOrGetProp(condition, blindScope, outcome);
case Token.ASSIGN:
return firstPreciserScopeKnowingConditionOutcome(
condition.getFirstChild(),
firstPreciserScopeKnowingConditionOutcome(
condition.getFirstChild().getNext(), blindScope, outcome),
outcome);
case Token.NOT:
return firstPreciserScopeKnowingConditionOutcome(
condition.getFirstChild(), blindScope, !outcome);
case Token.LE:
case Token.LT:
case Token.GE:
case Token.GT:
if (outcome) {
return caseEquality(condition, blindScope, INEQ);
}
break;
case Token.INSTANCEOF:
return caseInstanceOf(
condition.getFirstChild(), condition.getLastChild(), blindScope,
outcome);
case Token.IN:
if (outcome && condition.getFirstChild().isString()) {
return caseIn(condition.getLastChild(),
condition.getFirstChild().getString(), blindScope);
}
break;
case Token.CASE:
Node left =
condition.getParent().getFirstChild(); // the switch condition
Node right = condition.getFirstChild();
if (outcome) {
return caseEquality(left, right, blindScope, SHEQ);
} else {
return caseEquality(left, right, blindScope, SHNE);
}
}
return nextPreciserScopeKnowingConditionOutcome(
condition, blindScope, outcome);
}
private FlowScope caseEquality(Node condition, FlowScope blindScope,
Function<TypePair, TypePair> merging) {
return caseEquality(condition.getFirstChild(), condition.getLastChild(),
blindScope, merging);
}
private FlowScope caseEquality(Node left, Node right, FlowScope blindScope,
Function<TypePair, TypePair> merging) {
// left type
JSType leftType = getTypeIfRefinable(left, blindScope);
boolean leftIsRefineable;
if (leftType != null) {
leftIsRefineable = true;
} else {
leftIsRefineable = false;
leftType = left.getJSType();
}
// right type
JSType rightType = getTypeIfRefinable(right, blindScope);
boolean rightIsRefineable;
if (rightType != null) {
rightIsRefineable = true;
} else {
rightIsRefineable = false;
rightType = right.get
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> {@code assignLhsChild != null}, then the node being traversed is
* a descendant of the first child of an ASSIGN node. assignLhsChild's
* parent is this ASSIGN node.
*/
private Node assignLhsChild = null;
CheckGlobalThis(AbstractCompiler compiler) {
this.compiler = compiler;
}
/**
* Since this pass reports errors only when a global {@code this} keyword
* is encountered, there is no reason to traverse non global contexts.
*/
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
if (n.isFunction()) {
// Don't traverse functions that are constructors or have the @this
// or @override annotation.
JSDocInfo jsDoc = getFunctionJsDocInfo(n);
if (jsDoc != null &&
(jsDoc.isConstructor() ||
jsDoc.isInterface() ||
jsDoc.hasThisType() ||
jsDoc.isOverride())) {
return false;
}
// Don't traverse functions unless they would normally
// be able to have a @this annotation associated with them. e.g.,
// var a = function() { }; // or
// function a() {} // or
// a.x = function() {}; // or
// var a = {x: function() {}};
int pType = parent.getType();
if (!(pType == Token.BLOCK ||
pType == Token.SCRIPT ||
pType == Token.NAME ||
pType == Token.ASSIGN ||
// object literal keys
pType == Token.STRING_KEY)) {
return false;
}
// Don't traverse functions that are getting lent to a prototype.
Node gramps = parent.getParent();
if (NodeUtil.isObjectLitKey(parent, gramps)) {
JSDocInfo maybeLends = gramps.getJSDocInfo();
if (maybeLends != null &&
maybeLends.getLendsName() != null &&
maybeLends.getLendsName().endsWith(".prototype")) {
return false;
}
}
}
if (parent != null && parent.isAssign()) {
Node lhs = parent.getFirstChild();
Node rhs = lhs.getNext();
if (n == lhs) {
// Always traverse the left side of the assignment. To handle
// nested assignments properly (e.g., (a = this).property = c;),
// assignLhsChild should not be overridden.
if (
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>assignLhsChild == null) {
assignLhsChild = lhs;
}
} else {
// Only traverse the right side if it's not an assignment to a prototype
// property or subproperty.
if (NodeUtil.isGet(lhs)) {
if (lhs.isGetProp() &&
lhs.getLastChild().getString().equals("prototype")) {
return false;
}
Node llhs = lhs.getFirstChild();
if (llhs.isGetProp() &&
llhs.getLastChild().getString().equals("prototype")) {
return false;
}
}
}
}
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isThis() && shouldReportThis(n, parent)) {
compiler.report(t.makeError(n, GLOBAL_THIS));
}
if (n == assignLhsChild) {
assignLhsChild = null;
}
}
private boolean shouldReportThis(Node n, Node parent) {
if (assignLhsChild != null) {
// Always report a THIS on the left side of an assign.
return true;
}
// Also report a THIS with a property access.
return parent != null && NodeUtil.isGet(parent);
}
/**
* Gets a function's JSDoc information, if it has any. Checks for a few
* patterns (ellipses show where JSDoc would be):
* <pre>
* ... function() {}
* ... x = function() {};
* var ... x = function() {};
* ... var x = function() {};
* </pre>
*/
private JSDocInfo getFunctionJsDocInfo(Node n) {
JSDocInfo jsDoc = n.getJSDocInfo();
Node parent = n.getParent();
if (jsDoc == null) {
int parentType = parent.getType();
if (parentType == Token.NAME || parentType == Token.ASSIGN) {
jsDoc = parent.getJSDocInfo();
if (jsDoc == null && parentType == Token.NAME) {
Node gramps = parent.getParent();
if (gramps.isVar()) {
jsDoc = gramps.getJSDocInfo();
}
}
}
}
return jsDoc;
}
}
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>.remove(fullName);
}
}
break;
}
}
if (!t.inGlobalScope() &&
n.getJSDocInfo() != null && n.getJSDocInfo().isDefine()) {
// warn about @define annotations in local scopes
compiler.report(
t.makeError(n, NON_GLOBAL_DEFINE_INIT_ERROR, ""));
}
if (lvalueToRemoveLater == n) {
lvalueToRemoveLater = null;
if (n.isAssign()) {
Node last = n.getLastChild();
n.removeChild(last);
parent.replaceChild(n, last);
} else {
Preconditions.checkState(n.isName());
n.removeChild(n.getFirstChild());
}
compiler.reportCodeChange();
}
if (n.isCall()) {
if (t.inGlobalScope()) {
// If there's a function call in the global scope,
// we just say it's unsafe and freeze all the defines.
//
// NOTE(nicksantos): We could be a lot smarter here. For example,
// ReplaceOverriddenVars keeps a call graph of all functions and
// which functions/variables that they reference, and tries
// to statically determine which functions are "safe" and which
// are not. But this would be overkill, especially because
// the intended use of defines is with config_files, where
// all the defines are at the top of the bundle.
for (DefineInfo info : assignableDefines.values()) {
setDefineInfoNotAssignable(info, t);
}
assignableDefines.clear();
}
}
updateAssignAllowedStack(n, false);
}
/**
* Determines whether assignment to a define should be allowed
* in the subtree of the given node, and if not, records that fact.
*
* @param n The node whose subtree we're about to enter or exit.
* @param entering True if we're entering the subtree, false otherwise.
*/
private void updateAssignAllowedStack(Node n, boolean entering) {
switch (n.getType()) {
case Token.CASE:
case Token.FOR:
case Token.FUNCTION:
case Token.HOOK:
case Token.IF:
case Token.SWITCH:
case Token.WHILE:
if (entering) {
assignAllowed.push(0);
} else {
assignAllowed.remove();
}
break;
}
}
/**
* Determines whether assignment to a define
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> refinedScope) {
curNode = node;
pushScope(refinedScope);
traverseBranch(node, parent);
popScope();
} else {
traverseBranch(node, parent);
}
}
/**
* Gets the compiler.
*/
public Compiler getCompiler() {
// TODO(nicksantos): Remove this type cast. This is just temporary
// while refactoring.
return (Compiler) compiler;
}
/**
* Gets the current line number, or zero if it cannot be determined. The line
* number is retrieved lazily as a running time optimization.
*/
public int getLineNumber() {
Node cur = curNode;
while (cur != null) {
int line = cur.getLineno();
if (line >=0) {
return line;
}
cur = cur.getParent();
}
return 0;
}
/**
* Gets the current input source name.
*
* @return A string that may be empty, but not null
*/
public String getSourceName() {
return sourceName;
}
/**
* Gets the current input source.
*/
public CompilerInput getInput() {
return compiler.getInput(inputId);
}
/**
* Gets the current input module.
*/
public JSModule getModule() {
CompilerInput input = getInput();
return input == null ? null : input.getModule();
}
/** Returns the node currently being traversed. */
public Node getCurrentNode() {
return curNode;
}
/**
* Traverses a node recursively.
*/
public static void traverse(
AbstractCompiler compiler, Node root, Callback cb) {
NodeTraversal t = new NodeTraversal(compiler, cb);
t.traverse(root);
}
/**
* Traverses a list of node trees.
*/
public static void traverseRoots(
AbstractCompiler compiler, List<Node> roots, Callback cb) {
NodeTraversal t = new NodeTraversal(compiler, cb);
t.traverseRoots(roots);
}
public static void traverseRoots(
AbstractCompiler compiler, Callback cb, Node ... roots) {
NodeTraversal t = new NodeTraversal(compiler, cb);
t.traverseRoots(roots);
}
/**
* Traverses a branch.
*/
@SuppressWarnings("fallthrough")
private void traverseBranch(Node n, Node parent) {
int type = n.getType();
if (type == Token.SCRIPT) {
inputId = n.getInputId();
source
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>Name = getSourceName(n);
}
curNode = n;
if (!callback.shouldTraverse(this, n, parent)) return;
switch (type) {
case Token.FUNCTION:
traverseFunction(n, parent);
break;
default:
for (Node child = n.getFirstChild(); child != null; ) {
// child could be replaced, in which case our child node
// would no longer point to the true next
Node next = child.getNext();
traverseBranch(child, n);
child = next;
}
break;
}
curNode = n;
callback.visit(this, n, parent);
}
/**
* Traverses a function.
*/
private void traverseFunction(Node n, Node parent) {
Preconditions.checkState(n.getChildCount() == 3);
Preconditions.checkState(n.isFunction());
final Node fnName = n.getFirstChild();
boolean isFunctionExpression = (parent != null)
&& NodeUtil.isFunctionExpression(n);
if (!isFunctionExpression) {
// Functions declarations are in the scope containing the declaration.
traverseBranch(fnName, n);
}
curNode = n;
pushScope(n);
if (isFunctionExpression) {
// Function expression names are only accessible within the function
// scope.
traverseBranch(fnName, n);
}
final Node args = fnName.getNext();
final Node body = args.getNext();
// Args
traverseBranch(args, n);
// Body
Preconditions.checkState(body.getNext() == null &&
body.isBlock(), body);
traverseBranch(body, n);
popScope();
}
/** Examines the functions stack for the last instance of a function node. */
@SuppressWarnings("unchecked")
public Node getEnclosingFunction() {
if (scopes.size() + scopeRoots.size() < 2) {
return null;
} else {
if (scopeRoots.isEmpty()) {
return scopes.peek().getRootNode();
} else {
return scopeRoots.peek();
}
}
}
/** Creates a new scope (e.g. when entering a function). */
private void pushScope(Node node) {
Preconditions.checkState(curNode != null);
scopeRoots.push(node);
cfgs.push(null);
if (scopeCallback != null) {
scopeCallback.enterScope(this);
}
}
/** Creates a new scope (e.g. when entering
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* John Lenz
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino;
import com.google.common.base.Preconditions;
import java.util.List;
/**
* An AST construction helper class
* @author johnlenz@google.com (John Lenz)
*/
public class IR {
private IR() {}
public static Node empty() {
return new Node(Token.EMPTY);
}
public static Node function(Node name, Node params, Node body) {
Preconditions.checkState(name.isName());
Preconditions.checkState(params.isParamList());
Preconditions.checkState(body.isBlock());
return new Node(Token.FUNCTION, name, params, body);
}
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> public static Node paramList() {
return new Node(Token.PARAM_LIST);
}
public static Node paramList(Node param) {
Preconditions.checkState(param.isName());
return new Node(Token.PARAM_LIST, param);
}
public static Node paramList(Node ... params) {
Node paramList = paramList();
for (Node param : params) {
Preconditions.checkState(param.isName());
paramList.addChildToBack(param);
}
return paramList;
}
public static Node paramList(List<Node> params) {
Node paramList = paramList();
for (Node param : params) {
Preconditions.checkState(param.isName());
paramList.addChildToBack(param);
}
return paramList;
}
public static Node block() {
Node block = new Node(Token.BLOCK);
return block;
}
public static Node block(Node stmt) {
Preconditions.checkState(mayBeStatement(stmt));
Node block = new Node(Token.BLOCK, stmt);
return block;
}
public static Node block(Node ... stmts) {
Node block = new Node(Token.BLOCK);
for (Node stmt : stmts) {
Preconditions.checkState(mayBeStatement(stmt));
block.addChildToBack(stmt);
}
return block;
}
private static Node blockUnchecked(Node stmt) {
return new Node(Token.BLOCK, stmt);
}
public static Node script(Node ... stmts) {
// TODO(johnlenz): finish setting up the SCRIPT node
Node block = new Node(Token.SCRIPT);
for (Node stmt : stmts) {
Preconditions.checkState(mayBeStatement(stmt));
block.addChildToBack(stmt);
}
return block;
}
public static Node var(Node name, Node value) {
Preconditions.checkState(name.isName() && !name.hasChildren());
Preconditions.checkState(mayBeExpression(value));
name.addChildToFront(value);
return var(name);
}
public static Node var(Node name) {
Preconditions.checkState(name.isName());
return new Node(Token.VAR, name);
}
public static Node returnNode() {
return new Node(Token.RETURN);
}
public static Node returnNode(Node expr) {
Preconditions.checkState(mayBeExpression(expr));
return new Node(Token.RETURN, expr);
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> }
public static Node throwNode(Node expr) {
Preconditions.checkState(mayBeExpression(expr));
return new Node(Token.THROW, expr);
}
public static Node exprResult(Node expr) {
Preconditions.checkState(mayBeExpression(expr));
return new Node(Token.EXPR_RESULT, expr);
}
public static Node ifNode(Node cond, Node then) {
Preconditions.checkState(mayBeExpression(cond));
Preconditions.checkState(then.isBlock());
return new Node(Token.IF, cond, then);
}
public static Node ifNode(Node cond, Node then, Node elseNode) {
Preconditions.checkState(mayBeExpression(cond));
Preconditions.checkState(then.isBlock());
Preconditions.checkState(elseNode.isBlock());
return new Node(Token.IF, cond, then, elseNode);
}
public static Node doNode(Node body, Node cond) {
Preconditions.checkState(body.isBlock());
Preconditions.checkState(mayBeExpression(cond));
return new Node(Token.DO, body, cond);
}
public static Node forIn(Node target, Node cond, Node body) {
Preconditions.checkState(target.isVar() || mayBeExpression(target));
Preconditions.checkState(mayBeExpression(cond));
Preconditions.checkState(body.isBlock());
return new Node(Token.FOR, target, cond, body);
}
public static Node forNode(Node init, Node cond, Node incr, Node body) {
Preconditions.checkState(init.isVar() || mayBeExpressionOrEmpty(init));
Preconditions.checkState(mayBeExpressionOrEmpty(cond));
Preconditions.checkState(mayBeExpressionOrEmpty(incr));
Preconditions.checkState(body.isBlock());
return new Node(Token.FOR, init, cond, incr, body);
}
public static Node switchNode(Node cond, Node ... cases) {
Preconditions.checkState(mayBeExpression(cond));
Node switchNode = new Node(Token.SWITCH, cond);
for (Node caseNode : cases) {
Preconditions.checkState(caseNode.isCase() || caseNode.isDefaultCase());
switchNode.addChildToBack(caseNode);
}
return switchNode;
}
public static Node caseNode(Node expr, Node body) {
Preconditions.checkState(mayBeExpression(expr));
Preconditions.checkState(body.isBlock());
body.putBoolean
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>Prop(Node.SYNTHETIC_BLOCK_PROP, true);
return new Node(Token.CASE, expr, body);
}
public static Node defaultCase(Node body) {
Preconditions.checkState(body.isBlock());
body.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true);
return new Node(Token.DEFAULT_CASE, body);
}
public static Node label(Node name, Node stmt) {
// TODO(johnlenz): additional validation here.
Preconditions.checkState(name.isLabelName());
Preconditions.checkState(mayBeStatement(stmt));
Node block = new Node(Token.LABEL, name, stmt);
return block;
}
public static Node labelName(String name) {
Preconditions.checkState(!name.isEmpty());
return Node.newString(Token.LABEL_NAME, name);
}
public static Node tryFinally(Node tryBody, Node finallyBody) {
Preconditions.checkState(tryBody.isBlock());
Preconditions.checkState(finallyBody.isBlock());
Node catchBody = block().copyInformationFrom(tryBody);
return new Node(Token.TRY, tryBody, catchBody, finallyBody);
}
public static Node tryCatch(Node tryBody, Node catchNode) {
Preconditions.checkState(tryBody.isBlock());
Preconditions.checkState(catchNode.isCatch());
Node catchBody = blockUnchecked(catchNode).copyInformationFrom(catchNode);
return new Node(Token.TRY, tryBody, catchBody);
}
public static Node tryCatchFinally(
Node tryBody, Node catchNode, Node finallyBody) {
Preconditions.checkState(finallyBody.isBlock());
Node tryNode = tryCatch(tryBody, catchNode);
tryNode.addChildToBack(finallyBody);
return tryNode;
}
public static Node catchNode(Node expr, Node body) {
Preconditions.checkState(expr.isName());
Preconditions.checkState(body.isBlock());
return new Node(Token.CATCH, expr, body);
}
public static Node breakNode() {
return new Node(Token.BREAK);
}
public static Node breakNode(Node name) {
// TODO(johnlenz): additional validation here.
Preconditions.checkState(name.isLabelName());
return new Node(Token.BREAK, name);
}
public static Node continueNode() {
return new Node(Token.CONTINUE);
}
public static Node continueNode
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>(Node name) {
// TODO(johnlenz): additional validation here.
Preconditions.checkState(name.isLabelName());
return new Node(Token.CONTINUE, name);
}
//
public static Node call(Node target, Node ... args) {
Node call = new Node(Token.CALL, target);
for (Node arg : args) {
Preconditions.checkState(mayBeExpression(arg));
call.addChildToBack(arg);
}
return call;
}
public static Node newNode(Node target, Node ... args) {
Node newcall = new Node(Token.NEW, target);
for (Node arg : args) {
Preconditions.checkState(mayBeExpression(arg));
newcall.addChildToBack(arg);
}
return newcall;
}
public static Node name(String name) {
return Node.newString(Token.NAME, name);
}
public static Node getprop(Node target, Node prop) {
Preconditions.checkState(mayBeExpression(target));
Preconditions.checkState(prop.isString());
return new Node(Token.GETPROP, target, prop);
}
public static Node getelem(Node target, Node elem) {
Preconditions.checkState(mayBeExpression(target));
Preconditions.checkState(mayBeExpression(elem));
return new Node(Token.GETELEM, target, elem);
}
public static Node assign(Node target, Node expr) {
Preconditions.checkState(isAssignmentTarget(target));
Preconditions.checkState(mayBeExpression(expr));
return new Node(Token.ASSIGN, target, expr);
}
public static Node hook(Node cond, Node trueval, Node falseval) {
Preconditions.checkState(mayBeExpression(cond));
Preconditions.checkState(mayBeExpression(trueval));
Preconditions.checkState(mayBeExpression(falseval));
return new Node(Token.HOOK, cond, trueval, falseval);
}
public static Node comma(Node expr1, Node expr2) {
return binaryOp(Token.COMMA, expr1, expr2);
}
public static Node and(Node expr1, Node expr2) {
return binaryOp(Token.AND, expr1, expr2);
}
public static Node or(Node expr1, Node expr2) {
return binaryOp(Token.OR, expr1, expr2);
}
public static Node not(Node expr1) {
return unaryOp(Token.NOT, expr1
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>);
}
/**
* "=="
*/
public static Node eq(Node expr1, Node expr2) {
return binaryOp(Token.EQ, expr1, expr2);
}
/**
* "==="
*/
public static Node sheq(Node expr1, Node expr2) {
return binaryOp(Token.SHEQ, expr1, expr2);
}
public static Node voidNode(Node expr1) {
return unaryOp(Token.VOID, expr1);
}
public static Node neg(Node expr1) {
return unaryOp(Token.NEG, expr1);
}
public static Node pos(Node expr1) {
return unaryOp(Token.POS, expr1);
}
public static Node add(Node expr1, Node expr2) {
return binaryOp(Token.ADD, expr1, expr2);
}
public static Node sub(Node expr1, Node expr2) {
return binaryOp(Token.SUB, expr1, expr2);
}
// TODO(johnlenz): the rest of the ops
// literals
public static Node objectlit(Node ... propdefs) {
Node objectlit = new Node(Token.OBJECTLIT);
for (Node propdef : propdefs) {
Preconditions.checkState(
propdef.isStringKey() ||
propdef.isGetterDef() || propdef.isSetterDef());
Preconditions.checkState(propdef.hasOneChild());
objectlit.addChildToBack(propdef);
}
return objectlit;
}
// TODO(johnlenz): quoted props
public static Node propdef(Node string, Node value) {
Preconditions.checkState(string.isStringKey());
Preconditions.checkState(!string.hasChildren());
Preconditions.checkState(mayBeExpression(value));
string.addChildToFront(value);
return string;
}
public static Node arraylit(Node ... exprs) {
Node arraylit = new Node(Token.ARRAYLIT);
for (Node expr : exprs) {
Preconditions.checkState(mayBeExpressionOrEmpty(expr));
arraylit.addChildToBack(expr);
}
return arraylit;
}
public static Node regexp(Node expr) {
Preconditions.checkState(expr.isString());
return new Node(Token.REGEXP, expr);
}
public static Node regexp(Node expr, Node flags) {
Preconditions.checkState(expr.isString());
Preconditions.checkState(flags.isString());
return new Node
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>(Token.REGEXP, expr, flags);
}
public static Node string(String s) {
return Node.newString(s);
}
public static Node stringKey(String s) {
return Node.newString(Token.STRING_KEY, s);
}
public static Node number(double d) {
return Node.newNumber(d);
}
public static Node thisNode() {
return new Node(Token.THIS);
}
public static Node trueNode() {
return new Node(Token.TRUE);
}
public static Node falseNode() {
return new Node(Token.FALSE);
}
public static Node nullNode() {
return new Node(Token.NULL);
}
// helper methods
private static Node binaryOp(int token, Node expr1, Node expr2) {
Preconditions.checkState(mayBeExpression(expr1));
Preconditions.checkState(mayBeExpression(expr2));
return new Node(token, expr1, expr2);
}
private static Node unaryOp(int token, Node expr) {
Preconditions.checkState(mayBeExpression(expr));
return new Node(token, expr);
}
private static boolean mayBeExpressionOrEmpty(Node n) {
return n.isEmpty() || mayBeExpression(n);
}
private static boolean isAssignmentTarget(Node n) {
return n.isName() || n.isGetProp() || n.isGetElem();
}
// NOTE: some nodes are neither statements nor expression nodes:
// SCRIPT, LABEL_NAME, PARAM_LIST, CASE, DEFAULT_CASE, CATCH
// GETTER_DEF, SETTER_DEF
/**
* It isn't possible to always determine if a detached node is a expression,
* so make a best guess.
*/
private static boolean mayBeStatement(Node n) {
switch (n.getType()) {
case Token.EMPTY:
case Token.FUNCTION:
// EMPTY and FUNCTION are used both in expression and statement
// contexts
return true;
case Token.BLOCK:
case Token.BREAK:
case Token.CONST:
case Token.CONTINUE:
case Token.DEBUGGER:
case Token.DO:
case Token.EXPR_RESULT:
case Token.FOR:
case Token.IF:
case Token.LABEL:
case Token.RETURN:
case Token.SWITCH:
case Token.THROW:
case Token.TRY:
case Token.VAR:
case Token.WHILE:
case Token.
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>WITH:
return true;
default:
return false;
}
}
/**
* It isn't possible to always determine if a detached node is a expression,
* so make a best guess.
*/
private static boolean mayBeExpression(Node n) {
switch (n.getType()) {
case Token.FUNCTION:
// FUNCTION is used both in expression and statement
// contexts.
return true;
case Token.ADD:
case Token.AND:
case Token.ARRAYLIT:
case Token.ASSIGN:
case Token.ASSIGN_BITOR:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_BITAND:
case Token.ASSIGN_LSH:
case Token.ASSIGN_RSH:
case Token.ASSIGN_URSH:
case Token.ASSIGN_ADD:
case Token.ASSIGN_SUB:
case Token.ASSIGN_MUL:
case Token.ASSIGN_DIV:
case Token.ASSIGN_MOD:
case Token.BITAND:
case Token.BITOR:
case Token.BITNOT:
case Token.BITXOR:
case Token.CALL:
case Token.COMMA:
case Token.DEC:
case Token.DELPROP:
case Token.DIV:
case Token.EQ:
case Token.FALSE:
case Token.GE:
case Token.GETPROP:
case Token.GETELEM:
case Token.GT:
case Token.HOOK:
case Token.IN:
case Token.INC:
case Token.INSTANCEOF:
case Token.LE:
case Token.LSH:
case Token.LT:
case Token.MOD:
case Token.MUL:
case Token.NAME:
case Token.NE:
case Token.NEG:
case Token.NEW:
case Token.NOT:
case Token.NUMBER:
case Token.NULL:
case Token.OBJECTLIT:
case Token.OR:
case Token.POS:
case Token.REGEXP:
case Token.RSH:
case Token.SHEQ:
case Token.SHNE:
case Token.STRING:
case Token.SUB:
case Token.THIS:
case Token.TYPEOF:
case Token.TRUE:
case Token.URSH:
case Token.VOID:
return true;
default:
return false;
}
}
}
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> dependency.
t.report(n, VIOLATED_MODULE_DEP_ERROR,
currModule.getName(), varModule.getName(), varName);
} else {
// The variable reference is between two modules that have no
// dependency relationship. This should probably be considered an
// error, but just issue a warning for now.
t.report(n, MISSING_MODULE_DEP_ERROR,
currModule.getName(), varModule.getName(), varName);
}
} else {
t.report(n, STRICT_MODULE_DEP_ERROR,
currModule.getName(), varModule.getName(), varName);
}
}
}
}
/**
* Create a new variable in a synthetic script. This will prevent
* subsequent compiler passes from crashing.
*/
private void createSynthesizedExternVar(String varName) {
Node nameNode = IR.name(varName);
// Mark the variable as constant if it matches the coding convention
// for constant vars.
// NOTE(nicksantos): honestly, I'm not sure how much this matters.
// AFAIK, all people who use the CONST coding convention also
// compile with undeclaredVars as errors. We have some test
// cases for this configuration though, and it makes them happier.
if (compiler.getCodingConvention().isConstant(varName)) {
nameNode.putBooleanProp(Node.IS_CONSTANT_NAME, true);
}
getSynthesizedExternsRoot().addChildToBack(
IR.var(nameNode));
varsToDeclareInExterns.remove(varName);
compiler.reportCodeChange();
}
/**
* A check for name references in the externs inputs. These used to prevent
* a variable from getting renamed, but no longer have any effect.
*/
private class NameRefInExternsCheck extends AbstractPostOrderCallback {
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isName()) {
switch (parent.getType()) {
case Token.VAR:
case Token.FUNCTION:
case Token.PARAM_LIST:
// These are okay.
break;
case Token.GETPROP:
if (n == parent.getFirstChild()) {
Scope scope = t.getScope();
Scope.Var var = scope.getVar(n.getString());
if (var == null) {
t.report(n, UNDEFINED_EXTERN_VAR_ERROR, n.getString());
varsToDeclareInEx
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isName()) {
Var v;
if (n.getString().equals("arguments")) {
v = t.getScope().getArgumentsVar();
} else {
v = t.getScope().getVar(n.getString());
}
if (v != null && varFilter.apply(v)) {
addReference(t, v, new Reference(n, t, blockStack.peek()));
}
}
if (isBlockBoundary(n, parent)) {
blockStack.pop();
}
}
/**
* Updates block stack and invokes any additional behavior.
*/
@Override
public void enterScope(NodeTraversal t) {
Node n = t.getScope().getRootNode();
BasicBlock parent = blockStack.isEmpty() ? null : blockStack.peek();
blockStack.push(new BasicBlock(parent, n));
}
/**
* Updates block stack and invokes any additional behavior.
*/
@Override
public void exitScope(NodeTraversal t) {
blockStack.pop();
if (t.getScope().isGlobal()) {
// Update global scope reference lists when we are done with it.
compiler.updateGlobalVarReferences(referenceMap, t.getScopeRoot());
behavior.afterExitScope(t, compiler.getGlobalVarReferences());
} else {
behavior.afterExitScope(t, new ReferenceMapWrapper(referenceMap));
}
}
/**
* Updates block stack.
*/
@Override
public boolean shouldTraverse(NodeTraversal nodeTraversal, Node n,
Node parent) {
// If node is a new basic block, put on basic block stack
if (isBlockBoundary(n, parent)) {
blockStack.push(new BasicBlock(blockStack.peek(), n));
}
return true;
}
/**
* @return true if this node marks the start of a new basic block
*/
private static boolean isBlockBoundary(Node n, Node parent) {
if (parent != null) {
switch (parent.getType()) {
case Token.DO:
case Token.FOR:
case Token.TRY:
case Token.WHILE:
case Token.WITH:
// NOTE: TRY has up to 3 child blocks:
// TRY
// BLOCK
// BLOCK
// CATCH
// BLOCK
// Note that there is an explicit CATCH token but no explicit
// FINALLY token. For simplicity,
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> we consider each BLOCK
// a separate basic BLOCK.
return true;
case Token.AND:
case Token.HOOK:
case Token.IF:
case Token.OR:
// The first child of a conditional is not a boundary,
// but all the rest of the children are.
return n != parent.getFirstChild();
}
}
return n.isCase();
}
private void addReference(NodeTraversal t, Var v, Reference reference) {
// Create collection if none already
ReferenceCollection referenceInfo = referenceMap.get(v);
if (referenceInfo == null) {
referenceInfo = new ReferenceCollection();
referenceMap.put(v, referenceInfo);
}
// Add this particular reference
referenceInfo.add(reference, t, v);
}
interface ReferenceMap {
ReferenceCollection getReferences(Var var);
}
private static class ReferenceMapWrapper implements ReferenceMap {
private final Map<Var, ReferenceCollection> referenceMap;
public ReferenceMapWrapper(Map<Var, ReferenceCollection> referenceMap) {
this.referenceMap = referenceMap;
}
@Override
public ReferenceCollection getReferences(Var var) {
return referenceMap.get(var);
}
}
/**
* Way for callers to add specific behavior during traversal that
* utilizes the built-up reference information.
*/
interface Behavior {
/**
* Called after we finish with a scope.
*/
void afterExitScope(NodeTraversal t, ReferenceMap referenceMap);
}
static Behavior DO_NOTHING_BEHAVIOR = new Behavior() {
@Override
public void afterExitScope(NodeTraversal t, ReferenceMap referenceMap) {}
};
/**
* A collection of references. Can be subclassed to apply checks or
* store additional state when adding.
*/
static class ReferenceCollection implements Iterable<Reference> {
List<Reference> references = Lists.newArrayList();
@Override
public Iterator<Reference> iterator() {
return references.iterator();
}
void add(Reference reference, NodeTraversal t, Var v) {
references.add(reference);
}
/**
* Determines if the variable for this reference collection is
* "well-defined." A variable is well-defined if we can prove at
* compile-time that it's assigned a value before it's used.
*
* Notice that if this function returns false, this doesn't imply that the
* variable is used before it's assigned. It just means that we don't
* have enough information to make a
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> > 0 && references.get(0).isInitializingDeclaration()) {
return true;
}
return false;
}
}
/**
* Represents a single declaration or reference to a variable.
*/
static final class Reference implements StaticReference<JSType> {
private static final Set<Integer> DECLARATION_PARENTS =
ImmutableSet.of(Token.VAR, Token.FUNCTION, Token.CATCH);
private final Node nameNode;
private final BasicBlock basicBlock;
private final Scope scope;
private final InputId inputId;
private final StaticSourceFile sourceFile;
Reference(Node nameNode, NodeTraversal t,
BasicBlock basicBlock) {
this(nameNode, basicBlock, t.getScope(), t.getInput().getInputId());
}
// Bleeding functions are weird, because the declaration does
// not appear inside their scope. So they need their own constructor.
static Reference newBleedingFunction(NodeTraversal t,
BasicBlock basicBlock, Node func) {
return new Reference(func.getFirstChild(),
basicBlock, t.getScope(), t.getInput().getInputId());
}
/**
* Creates a variable reference in a given script file name, used in tests.
*
* @return The created reference.
*/
@VisibleForTesting
static Reference createRefForTest(CompilerInput input) {
return new Reference(new Node(Token.NAME), null, null,
input.getInputId());
}
private Reference(Node nameNode,
BasicBlock basicBlock, Scope scope, InputId inputId) {
this.nameNode = nameNode;
this.basicBlock = basicBlock;
this.scope = scope;
this.inputId = inputId;
this.sourceFile = nameNode.getStaticSourceFile();
}
/**
* Makes a copy of the current reference using a new Scope instance.
*/
Reference cloneWithNewScope(Scope newScope) {
return new Reference(nameNode, basicBlock, newScope, inputId);
}
@Override
public Var getSymbol() {
return scope.getVar(nameNode.getString());
}
@Override
public Node getNode() {
return nameNode;
}
public InputId getInputId() {
return inputId;
}
@Override
public StaticSourceFile getSourceFile() {
return sourceFile;
}
boolean isDeclaration() {
Node parent = getParent();
Node grandparent = parent.getParent();
return DECLARATION_PARENTS.contains(parent.getType())
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> ||
parent.isParamList() &&
grandparent.isFunction();
}
boolean isVarDeclaration() {
return getParent().isVar();
}
boolean isHoistedFunction() {
return NodeUtil.isHoistedFunctionDeclaration(getParent());
}
/**
* Determines whether the variable is initialized at the declaration.
*/
boolean isInitializingDeclaration() {
// VAR is the only type of variable declaration that may not initialize
// its variable. Catch blocks, named functions, and parameters all do.
return isDeclaration() &&
!getParent().isVar() ||
nameNode.getFirstChild() != null;
}
/**
* @return For an assignment, variable declaration, or function declaration
* return the assigned value, otherwise null.
*/
Node getAssignedValue() {
Node parent = getParent();
return (parent.isFunction())
? parent : NodeUtil.getAssignedValue(nameNode);
}
BasicBlock getBasicBlock() {
return basicBlock;
}
Node getParent() {
return getNode().getParent();
}
Node getGrandparent() {
Node parent = getParent();
return parent == null ? null : parent.getParent();
}
private static boolean isLhsOfForInExpression(Node n) {
Node parent = n.getParent();
if (parent.isVar()) {
return isLhsOfForInExpression(parent);
}
return NodeUtil.isForIn(parent) && parent.getFirstChild() == n;
}
boolean isSimpleAssignmentToName() {
Node parent = getParent();
return parent.isAssign()
&& parent.getFirstChild() == nameNode;
}
boolean isLvalue() {
Node parent = getParent();
int parentType = parent.getType();
return (parentType == Token.VAR && nameNode.getFirstChild() != null)
|| parentType == Token.INC
|| parentType == Token.DEC
|| (NodeUtil.isAssignmentOp(parent)
&& parent.getFirstChild() == nameNode)
|| isLhsOfForInExpression(nameNode);
}
Scope getScope() {
return scope;
}
}
/**
* Represents a section of code that is uninterrupted by control structures
* (conditional or iterative logic).
*/
static final class BasicBlock {
private final BasicBlock parent;
/**
* Determines whether the block may not be part of the normal control flow,
* but instead "hoisted" to the top of the scope.
*/
private final boolean isHoisted;
/**
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>
* Whether this block denotes a function scope.
*/
private final boolean isFunction;
/**
* Whether this block denotes a loop.
*/
private final boolean isLoop;
/**
* Creates a new block.
* @param parent The containing block.
* @param root The root node of the block.
*/
BasicBlock(BasicBlock parent, Node root) {
this.parent = parent;
// only named functions may be hoisted.
this.isHoisted = NodeUtil.isHoistedFunctionDeclaration(root);
this.isFunction = root.isFunction();
if (root.getParent() != null) {
int pType = root.getParent().getType();
this.isLoop = pType == Token.DO ||
pType == Token.WHILE ||
pType == Token.FOR;
} else {
this.isLoop = false;
}
}
BasicBlock getParent() {
return parent;
}
/**
* Determines whether this block is equivalent to the very first block that
* is created when reference collection traversal enters global scope. Note
* that when traversing a single script in a hot-swap fashion a new instance
* of {@code BasicBlock} is created.
*
* @return true if this is global scope block.
*/
boolean isGlobalScopeBlock() {
return getParent() == null;
}
/**
* Determines whether this block is guaranteed to begin executing before
* the given block does.
*/
boolean provablyExecutesBefore(BasicBlock thatBlock) {
// If thatBlock is a descendant of this block, and there are no hoisted
// blocks between them, then this block must start before thatBlock.
BasicBlock currentBlock;
for (currentBlock = thatBlock;
currentBlock != null && currentBlock != this;
currentBlock = currentBlock.getParent()) {
if (currentBlock.isHoisted) {
return false;
}
}
if (currentBlock == this) {
return true;
}
if (isGlobalScopeBlock() && thatBlock.isGlobalScopeBlock()) {
return true;
}
return false;
}
}
}
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>Creator = new MemoizedScopeCreator(new TypedScopeCreator(compiler));
topScope = scopeCreator.createScope(externsAndJsRoot, null);
TypeInferencePass inference = new TypeInferencePass(compiler,
reverseInterpreter, topScope, scopeCreator);
inference.process(externsRoot, jsRoot);
process(externsRoot, jsRoot);
return topScope;
}
public void check(Node node, boolean externs) {
Preconditions.checkNotNull(node);
NodeTraversal t = new NodeTraversal(compiler, this, scopeCreator);
inExterns = externs;
t.traverseWithScope(node, topScope);
if (externs) {
inferJSDocInfo.process(node, null);
} else {
inferJSDocInfo.process(null, node);
}
}
private void checkNoTypeCheckSection(Node n, boolean enterSection) {
switch (n.getType()) {
case Token.SCRIPT:
case Token.BLOCK:
case Token.VAR:
case Token.FUNCTION:
case Token.ASSIGN:
JSDocInfo info = n.getJSDocInfo();
if (info != null && info.isNoTypeCheck()) {
if (enterSection) {
noTypeCheckSection++;
} else {
noTypeCheckSection--;
}
}
validator.setShouldReport(noTypeCheckSection == 0);
break;
}
}
private void report(NodeTraversal t, Node n, DiagnosticType diagnosticType,
String... arguments) {
if (noTypeCheckSection == 0) {
t.report(n, diagnosticType, arguments);
}
}
@Override
public boolean shouldTraverse(
NodeTraversal t, Node n, Node parent) {
checkNoTypeCheckSection(n, true);
switch (n.getType()) {
case Token.FUNCTION:
// normal type checking
final Scope outerScope = t.getScope();
final String functionPrivateName = n.getFirstChild().getString();
if (functionPrivateName != null && functionPrivateName.length() > 0 &&
outerScope.isDeclared(functionPrivateName, false) &&
// Ideally, we would want to check whether the type in the scope
// differs from the type being defined, but then the extern
// redeclarations of built-in types generates spurious warnings.
!(outerScope.getVar(
functionPrivateName).getType() instanceof FunctionType)) {
report(t, n, FUNCTION_MASKS
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>_VARIABLE, functionPrivateName);
}
// TODO(user): Only traverse the function's body. The function's
// name and arguments are traversed by the scope creator, and ideally
// should not be traversed by the type checker.
break;
}
return true;
}
/**
* This is the meat of the type checking. It is basically one big switch,
* with each case representing one type of parse tree node. The individual
* cases are usually pretty straightforward.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
* @param parent The parent of the node n.
*/
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
JSType childType;
JSType leftType, rightType;
Node left, right;
// To be explicitly set to false if the node is not typeable.
boolean typeable = true;
switch (n.getType()) {
case Token.NAME:
typeable = visitName(t, n, parent);
break;
case Token.PARAM_LIST:
// If this is under a FUNCTION node, it is a parameter list and can be
// ignored here.
if (!parent.isFunction()) {
ensureTyped(t, n, getJSType(n.getFirstChild()));
} else {
typeable = false;
}
break;
case Token.COMMA:
ensureTyped(t, n, getJSType(n.getLastChild()));
break;
case Token.TRUE:
case Token.FALSE:
ensureTyped(t, n, BOOLEAN_TYPE);
break;
case Token.THIS:
ensureTyped(t, n, t.getScope().getTypeOfThis());
break;
case Token.NULL:
ensureTyped(t, n, NULL_TYPE);
break;
case Token.NUMBER:
ensureTyped(t, n, NUMBER_TYPE);
break;
case Token.STRING:
ensureTyped(t, n, STRING_TYPE);
break;
case Token.STRING_KEY:
typeable = false;
break;
case Token.GETTER_DEF:
case Token.SETTER_DEF:
// Object literal keys are handled with OBJECTLIT
break;
case Token.ARRAYLIT:
ensureTyped(t, n, ARRAY_TYPE);
break;
case Token.REGEXP:
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> ensureTyped(t, n, REGEXP_TYPE);
break;
case Token.GETPROP:
visitGetProp(t, n, parent);
typeable = !(parent.isAssign() &&
parent.getFirstChild() == n);
break;
case Token.GETELEM:
visitGetElem(t, n);
// The type of GETELEM is always unknown, so no point counting that.
// If that unknown leaks elsewhere (say by an assignment to another
// variable), then it will be counted.
typeable = false;
break;
case Token.VAR:
visitVar(t, n);
typeable = false;
break;
case Token.NEW:
visitNew(t, n);
typeable = true;
break;
case Token.CALL:
visitCall(t, n);
typeable = !parent.isExprResult();
break;
case Token.RETURN:
visitReturn(t, n);
typeable = false;
break;
case Token.DEC:
case Token.INC:
left = n.getFirstChild();
validator.expectNumber(
t, left, getJSType(left), "increment/decrement");
ensureTyped(t, n, NUMBER_TYPE);
break;
case Token.NOT:
ensureTyped(t, n, BOOLEAN_TYPE);
break;
case Token.VOID:
ensureTyped(t, n, VOID_TYPE);
break;
case Token.TYPEOF:
ensureTyped(t, n, STRING_TYPE);
break;
case Token.BITNOT:
childType = getJSType(n.getFirstChild());
if (!childType.matchesInt32Context()) {
report(t, n, BIT_OPERATION, NodeUtil.opToStr(n.getType()),
childType.toString());
}
ensureTyped(t, n, NUMBER_TYPE);
break;
case Token.POS:
case Token.NEG:
left = n.getFirstChild();
validator.expectNumber(t, left, getJSType(left), "sign operator");
ensureTyped(t, n, NUMBER_TYPE);
break;
case Token.EQ:
case Token.NE: {
leftType = getJSType(n.getFirstChild());
rightType = getJSType(n.getLastChild());
JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined();
JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined();
TernaryValue result =
left
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>TypeRestricted.testForEquality(rightTypeRestricted);
if (result != TernaryValue.UNKNOWN) {
if (n.isNE()) {
result = result.not();
}
report(t, n, DETERMINISTIC_TEST, leftType.toString(),
rightType.toString(), result.toString());
}
ensureTyped(t, n, BOOLEAN_TYPE);
break;
}
case Token.SHEQ:
case Token.SHNE: {
leftType = getJSType(n.getFirstChild());
rightType = getJSType(n.getLastChild());
JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined();
JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined();
if (!leftTypeRestricted.canTestForShallowEqualityWith(
rightTypeRestricted)) {
report(t, n, DETERMINISTIC_TEST_NO_RESULT, leftType.toString(),
rightType.toString());
}
ensureTyped(t, n, BOOLEAN_TYPE);
break;
}
case Token.LT:
case Token.LE:
case Token.GT:
case Token.GE:
leftType = getJSType(n.getFirstChild());
rightType = getJSType(n.getLastChild());
if (rightType.isNumber()) {
validator.expectNumber(
t, n, leftType, "left side of numeric comparison");
} else if (leftType.isNumber()) {
validator.expectNumber(
t, n, rightType, "right side of numeric comparison");
} else if (leftType.matchesNumberContext() &&
rightType.matchesNumberContext()) {
// OK.
} else {
// Whether the comparison is numeric will be determined at runtime
// each time the expression is evaluated. Regardless, both operands
// should match a string context.
String message = "left side of comparison";
validator.expectString(t, n, leftType, message);
validator.expectNotNullOrUndefined(
t, n, leftType, message, getNativeType(STRING_TYPE));
message = "right side of comparison";
validator.expectString(t, n, rightType, message);
validator.expectNotNullOrUndefined(
t, n, rightType, message, getNativeType(STRING_TYPE));
}
ensureTyped(t, n, BOOLEAN_TYPE);
break;
case Token.IN:
left = n.getFirstChild();
right = n.getLastChild();
leftType = get
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>JSType(left);
rightType = getJSType(right);
validator.expectObject(t, n, rightType, "'in' requires an object");
validator.expectString(t, left, leftType, "left side of 'in'");
ensureTyped(t, n, BOOLEAN_TYPE);
break;
case Token.INSTANCEOF:
left = n.getFirstChild();
right = n.getLastChild();
leftType = getJSType(left);
rightType = getJSType(right).restrictByNotNullOrUndefined();
validator.expectAnyObject(
t, left, leftType, "deterministic instanceof yields false");
validator.expectActualObject(
t, right, rightType, "instanceof requires an object");
ensureTyped(t, n, BOOLEAN_TYPE);
break;
case Token.ASSIGN:
visitAssign(t, n);
typeable = false;
break;
case Token.ASSIGN_LSH:
case Token.ASSIGN_RSH:
case Token.ASSIGN_URSH:
case Token.ASSIGN_DIV:
case Token.ASSIGN_MOD:
case Token.ASSIGN_BITOR:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_BITAND:
case Token.ASSIGN_SUB:
case Token.ASSIGN_ADD:
case Token.ASSIGN_MUL:
case Token.LSH:
case Token.RSH:
case Token.URSH:
case Token.DIV:
case Token.MOD:
case Token.BITOR:
case Token.BITXOR:
case Token.BITAND:
case Token.SUB:
case Token.ADD:
case Token.MUL:
visitBinaryOperator(n.getType(), t, n);
break;
case Token.DELPROP:
ensureTyped(t, n, BOOLEAN_TYPE);
break;
case Token.CASE:
JSType switchType = getJSType(parent.getFirstChild());
JSType caseType = getJSType(n.getFirstChild());
validator.expectSwitchMatchesCase(t, n, switchType, caseType);
typeable = false;
break;
case Token.WITH: {
Node child = n.getFirstChild();
childType = getJSType(child);
validator.expectObject(
t, child, childType, "with requires an object");
typeable = false;
break;
}
case Token.FUNCTION:
visitFunction(t, n);
break;
// These nodes have no interesting type behavior.
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> case Token.LABEL:
case Token.LABEL_NAME:
case Token.SWITCH:
case Token.BREAK:
case Token.CATCH:
case Token.TRY:
case Token.SCRIPT:
case Token.EXPR_RESULT:
case Token.BLOCK:
case Token.EMPTY:
case Token.DEFAULT_CASE:
case Token.CONTINUE:
case Token.DEBUGGER:
case Token.THROW:
typeable = false;
break;
// These nodes require data flow analysis.
case Token.DO:
case Token.FOR:
case Token.IF:
case Token.WHILE:
typeable = false;
break;
// These nodes are typed during the type inference.
case Token.AND:
case Token.HOOK:
case Token.OBJECTLIT:
case Token.OR:
if (n.getJSType() != null) { // If we didn't run type inference.
ensureTyped(t, n);
} else {
// If this is an enum, then give that type to the objectlit as well.
if ((n.isObjectLit())
&& (parent.getJSType() instanceof EnumType)) {
ensureTyped(t, n, parent.getJSType());
} else {
ensureTyped(t, n);
}
}
if (n.isObjectLit()) {
for (Node key : n.children()) {
visitObjLitKey(t, key, n);
}
}
break;
default:
report(t, n, UNEXPECTED_TOKEN, Token.name(n.getType()));
ensureTyped(t, n);
break;
}
// Don't count externs since the user's code may not even use that part.
typeable = typeable && !inExterns;
if (typeable) {
doPercentTypedAccounting(t, n);
}
checkNoTypeCheckSection(n, false);
}
/**
* Counts the given node in the typed statistics.
* @param n a node that should be typed
*/
private void doPercentTypedAccounting(NodeTraversal t, Node n) {
JSType type = n.getJSType();
if (type == null) {
nullCount++;
} else if (type.isUnknownType()) {
if (reportUnknownTypes.isOn()) {
compiler.report(
t.makeError(n, reportUnknownTypes, UNKNOWN_EXPR_TYPE));
}
unknownCount++;
} else {
typedCount++;
}
}
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> * @param parent The parent of the node n.
* @return whether the node is typeable or not
*/
boolean visitName(NodeTraversal t, Node n, Node parent) {
// At this stage, we need to determine whether this is a leaf
// node in an expression (which therefore needs to have a type
// assigned for it) versus some other decorative node that we
// can safely ignore. Function names, arguments (children of LP nodes) and
// variable declarations are ignored.
// TODO(user): remove this short-circuiting in favor of a
// pre order traversal of the FUNCTION, CATCH, LP and VAR nodes.
int parentNodeType = parent.getType();
if (parentNodeType == Token.FUNCTION ||
parentNodeType == Token.CATCH ||
parentNodeType == Token.PARAM_LIST ||
parentNodeType == Token.VAR) {
return false;
}
JSType type = n.getJSType();
if (type == null) {
type = getNativeType(UNKNOWN_TYPE);
Var var = t.getScope().getVar(n.getString());
if (var != null) {
JSType varType = var.getType();
if (varType != null) {
type = varType;
}
}
}
ensureTyped(t, n, type);
return true;
}
/**
* Visits a GETPROP node.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
* @param parent The parent of <code>n</code>
*/
private void visitGetProp(NodeTraversal t, Node n, Node parent) {
// GETPROP nodes have an assigned type on their node by the scope creator
// if this is an enum declaration. The only namespaced enum declarations
// that we allow are of the form object.name = ...;
if (n.getJSType() != null && parent.isAssign()) {
return;
}
// obj.prop or obj.method()
// Lots of types can appear on the left, a call to a void function can
// never be on the left. getPropertyType will decide what is acceptable
// and what isn't.
Node property = n.getLastChild();
Node objNode = n.getFirstChild();
JSType childType = getJSType(objNode);
// TODO(user): remove in favor of flagging every property access on
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> whether this node is testing for the existence of a property.
* If true, we will not emit warnings about a missing property.
*
* @param getProp The GETPROP being tested.
*/
private boolean isPropertyTest(Node getProp) {
Node parent = getProp.getParent();
switch (parent.getType()) {
case Token.CALL:
return parent.getFirstChild() != getProp &&
compiler.getCodingConvention().isPropertyTestFunction(parent);
case Token.IF:
case Token.WHILE:
case Token.DO:
case Token.FOR:
return NodeUtil.getConditionExpression(parent) == getProp;
case Token.INSTANCEOF:
case Token.TYPEOF:
return true;
case Token.AND:
case Token.HOOK:
return parent.getFirstChild() == getProp;
case Token.NOT:
return parent.getParent().isOr() &&
parent.getParent().getFirstChild() == parent;
}
return false;
}
/**
* Visits a GETELEM node.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
*/
private void visitGetElem(NodeTraversal t, Node n) {
Node left = n.getFirstChild();
Node right = n.getLastChild();
validator.expectIndexMatch(t, n, getJSType(left), getJSType(right));
ensureTyped(t, n);
}
/**
* Visits a VAR node.
*
* @param t The node traversal object that supplies context, such as the
* scope chain to use in name lookups as well as error reporting.
* @param n The node being visited.
*/
private void visitVar(NodeTraversal t, Node n) {
// TODO(nicksantos): Fix this so that the doc info always shows up
// on the NAME node. We probably want to wait for the parser
// merge to fix this.
JSDocInfo varInfo = n.hasOneChild() ? n.getJSDocInfo() : null;
for (Node name : n.children()) {
Node value = name.getFirstChild();
// A null var would indicate a bug in the scope creation logic.
Var var = t.getScope().getVar(name.getString());
if (value != null) {
JSType valueType = getJSType(value);
JS
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>
Node function = t.getEnclosingFunction();
// This is a misplaced return, but the real JS will fail to compile,
// so let it go.
if (function == null) {
return;
}
JSType jsType = getJSType(function);
if (jsType.isFunctionType()) {
FunctionType functionType = jsType.toMaybeFunctionType();
JSType returnType = functionType.getReturnType();
// if no return type is specified, undefined must be returned
// (it's a void function)
if (returnType == null) {
returnType = getNativeType(VOID_TYPE);
}
// fetching the returned value's type
Node valueNode = n.getFirstChild();
JSType actualReturnType;
if (valueNode == null) {
actualReturnType = getNativeType(VOID_TYPE);
valueNode = n;
} else {
actualReturnType = getJSType(valueNode);
}
// verifying
validator.expectCanAssignTo(t, valueNode, actualReturnType, returnType,
"inconsistent return type");
}
}
/**
* This function unifies the type checking involved in the core binary
* operators and the corresponding assignment operators. The representation
* used internally is such that common code can handle both kinds of
* operators easily.
*
* @param op The operator.
* @param t The traversal object, needed to report errors.
* @param n The node being checked.
*/
private void visitBinaryOperator(int op, NodeTraversal t, Node n) {
Node left = n.getFirstChild();
JSType leftType = getJSType(left);
Node right = n.getLastChild();
JSType rightType = getJSType(right);
switch (op) {
case Token.ASSIGN_LSH:
case Token.ASSIGN_RSH:
case Token.LSH:
case Token.RSH:
case Token.ASSIGN_URSH:
case Token.URSH:
if (!leftType.matchesInt32Context()) {
report(t, left, BIT_OPERATION,
NodeUtil.opToStr(n.getType()), leftType.toString());
}
if (!rightType.matchesUint32Context()) {
report(t, right, BIT_OPERATION,
NodeUtil.opToStr(n.getType()), rightType.toString());
}
break;
case Token.ASSIGN_DIV:
case Token.ASSIGN_MOD:
case Token.ASSIGN_MUL:
case Token.
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>ASSIGN_SUB:
case Token.DIV:
case Token.MOD:
case Token.MUL:
case Token.SUB:
validator.expectNumber(t, left, leftType, "left operand");
validator.expectNumber(t, right, rightType, "right operand");
break;
case Token.ASSIGN_BITAND:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_BITOR:
case Token.BITAND:
case Token.BITXOR:
case Token.BITOR:
validator.expectBitwiseable(t, left, leftType,
"bad left operand to bitwise operator");
validator.expectBitwiseable(t, right, rightType,
"bad right operand to bitwise operator");
break;
case Token.ASSIGN_ADD:
case Token.ADD:
break;
default:
report(t, n, UNEXPECTED_TOKEN, Token.name(op));
}
ensureTyped(t, n);
}
/**
* <p>Checks enum aliases.
*
* <p>We verify that the enum element type of the enum used
* for initialization is a subtype of the enum element type of
* the enum the value is being copied in.</p>
*
* <p>Example:</p>
* <pre>var myEnum = myOtherEnum;</pre>
*
* <p>Enum aliases are irregular, so we need special code for this :(</p>
*
* @param value the value used for initialization of the enum
*/
private void checkEnumAlias(
NodeTraversal t, JSDocInfo declInfo, Node value) {
if (declInfo == null || !declInfo.hasEnumParameterType()) {
return;
}
JSType valueType = getJSType(value);
if (!valueType.isEnumType()) {
return;
}
EnumType valueEnumType = valueType.toMaybeEnumType();
JSType valueEnumPrimitiveType =
valueEnumType.getElementsType().getPrimitiveType();
validator.expectCanAssignTo(t, value, valueEnumPrimitiveType,
declInfo.getEnumParameterType().evaluate(t.getScope(), typeRegistry),
"incompatible enum element types");
}
/**
* This method gets the JSType from the Node argument and verifies that it is
* present.
*/
private JSType getJSType(Node n) {
JSType jsType = n.getJSType();
if (jsType == null) {
// TODO(n
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>/*
* Copyright 2010 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.jscomp.NodeTraversal.AbstractPostOrderCallback;
import com.google.javascript.jscomp.regex.RegExpTree;
import com.google.javascript.rhino.Token;
import com.google.javascript.rhino.Node;
/**
* Look for references to the global RegExp object that would cause
* regular expressions to be unoptimizable, and checks that regular expressions
* are syntactically valid.
*
* @author johnlenz@google.com (John Lenz)
*/
class CheckRegExp extends AbstractPostOrderCallback implements CompilerPass {
static final DiagnosticType REGEXP_REFERENCE =
DiagnosticType.warning("JSC_REGEXP_REFERENCE",
"References to the global RegExp object prevents " +
"optimization of regular expressions.");
static final DiagnosticType MALFORMED_REGEXP = DiagnosticType.warning(
"JSC_MALFORMED_REGEXP",
"Malformed Regular Expression: {0}");
private final AbstractCompiler compiler;
private boolean globalRegExpPropertiesUsed = false;
public boolean isGlobalRegExpPropertiesUsed() {
return globalRegExpPropertiesUsed;
}
public CheckRegExp(AbstractCompiler compiler) {
this.compiler = compiler;
}
@Override
public void process(Node externs, Node root) {
NodeTraversal.traverse(compiler, root, this);
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (NodeUtil.isReferenceName(n)) {
String name = n.getString();
if (name.equals("RegExp") && t.getScope().getVar(name) == null) {
int parentType = parent.getType();
boolean first = (n == parent.getFirstChild());
if (!((parentType == Token.NEW &&
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> first)
|| (parentType == Token.CALL && first)
|| (parentType == Token.INSTANCEOF && !first))) {
t.report(n, REGEXP_REFERENCE);
globalRegExpPropertiesUsed = true;
}
}
// Check the syntax of regular expression patterns.
} else if (n.isRegExp()) {
String pattern = n.getFirstChild().getString();
String flags = n.getChildCount() == 2
? n.getLastChild().getString() : "";
try {
RegExpTree.parseRegExp(pattern, flags);
} catch (IllegalArgumentException ex) {
t.report(n, MALFORMED_REGEXP, ex.getMessage());
}
}
}
}
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>StaticSourceFile;
import java.util.Set;
/**
* IRFactory transforms the new AST to the old AST.
*
*/
class IRFactory {
static final String GETTER_ERROR_MESSAGE =
"getters are not supported in older versions of JS. " +
"If you are targeting newer versions of JS, " +
"set the appropriate language_in option.";
static final String SETTER_ERROR_MESSAGE =
"setters are not supported in older versions of JS. " +
"If you are targeting newer versions of JS, " +
"set the appropriate language_in option.";
static final String SUSPICIOUS_COMMENT_WARNING =
"Non-JSDoc comment has annotations. " +
"Did you mean to start it with '/**'?";
private final String sourceString;
private final StaticSourceFile sourceFile;
private final String sourceName;
private final Config config;
private final ErrorReporter errorReporter;
private final TransformDispatcher transformDispatcher;
private static final ImmutableSet<String> ALLOWED_DIRECTIVES =
ImmutableSet.of("use strict");
private static final ImmutableSet<String> ES5_RESERVED_KEYWORDS =
ImmutableSet.of(
// From Section 7.6.1.2
"class", "const", "enum", "export", "extends", "import", "super");
private static final ImmutableSet<String> ES5_STRICT_RESERVED_KEYWORDS =
ImmutableSet.of(
// From Section 7.6.1.2
"class", "const", "enum", "export", "extends", "import", "super",
"implements", "interface", "let", "package", "private", "protected",
"public", "static", "yield");
private final Set<String> reservedKeywords;
private final Set<Comment> parsedComments = Sets.newHashSet();
// @license text gets appended onto the fileLevelJsDocBuilder as found,
// and stored in JSDocInfo for placeholder node.
Node rootNodeJsDocHolder = new Node(Token.SCRIPT);
Node.FileLevelJsDocBuilder fileLevelJsDocBuilder =
rootNodeJsDocHolder.getJsDocBuilderForNode();
JSDocInfo fileOverviewInfo = null;
// Use a template node for properties set on all nodes to minimize the
// memory footprint associated with these.
private Node templateNode;
// TODO(johnlenz): Consider creating a template pool for ORIGINALNAME_PROP.
private IRFactory(String
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> sourceString,
StaticSourceFile sourceFile,
Config config,
ErrorReporter errorReporter) {
this.sourceString = sourceString;
this.sourceFile = sourceFile;
// Sometimes this will be null in tests.
this.sourceName = sourceFile == null ? null : sourceFile.getName();
this.config = config;
this.errorReporter = errorReporter;
this.transformDispatcher = new TransformDispatcher();
// The template node properties are applied to all nodes in this transform.
this.templateNode = createTemplateNode();
switch (config.languageMode) {
case ECMASCRIPT3:
// Reserved words are handled by the Rhino parser.
reservedKeywords = null;
break;
case ECMASCRIPT5:
reservedKeywords = ES5_RESERVED_KEYWORDS;
break;
case ECMASCRIPT5_STRICT:
reservedKeywords = ES5_STRICT_RESERVED_KEYWORDS;
break;
default:
throw new IllegalStateException("unknown language mode");
}
}
// Create a template node to use as a source of common attributes, this allows
// the prop structure to be shared among all the node from this source file.
// This reduces the cost of these properties to O(nodes) to O(files).
private Node createTemplateNode() {
// The Node type choice is arbitrary.
Node templateNode = new Node(Token.SCRIPT);
templateNode.setStaticSourceFile(sourceFile);
return templateNode;
}
public static Node transformTree(AstRoot node,
StaticSourceFile sourceFile,
String sourceString,
Config config,
ErrorReporter errorReporter) {
IRFactory irFactory = new IRFactory(sourceString, sourceFile,
config, errorReporter);
Node irNode = irFactory.transform(node);
if (node.getComments() != null) {
for (Comment comment : node.getComments()) {
if (comment.getCommentType() == CommentType.JSDOC &&
!irFactory.parsedComments.contains(comment)) {
irFactory.handlePossibleFileOverviewJsDoc(comment, irNode);
} else if (comment.getCommentType() == CommentType.BLOCK_COMMENT) {
irFactory.handleBlockComment(comment);
}
}
}
irFactory.setFileOverviewJsDoc(irNode);
return irNode;
}
private void setFileOverviewJsDoc(Node irNode) {
// Only after we've seen all @fileoverview entries, attach the
// last one to the root node, and
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> copy the found license strings
// to that node.
JSDocInfo rootNodeJsDoc = rootNodeJsDocHolder.getJSDocInfo();
if (rootNodeJsDoc != null) {
irNode.setJSDocInfo(rootNodeJsDoc);
rootNodeJsDoc.setAssociatedNode(irNode);
}
if (fileOverviewInfo != null) {
if ((irNode.getJSDocInfo() != null) &&
(irNode.getJSDocInfo().getLicense() != null)) {
fileOverviewInfo.setLicense(irNode.getJSDocInfo().getLicense());
}
irNode.setJSDocInfo(fileOverviewInfo);
fileOverviewInfo.setAssociatedNode(irNode);
}
}
private Node transformBlock(AstNode node) {
Node irNode = transform(node);
if (!irNode.isBlock()) {
if (irNode.isEmpty()) {
irNode.setType(Token.BLOCK);
irNode.setWasEmptyNode(true);
} else {
Node newBlock = newNode(Token.BLOCK, irNode);
newBlock.setLineno(irNode.getLineno());
newBlock.setCharno(irNode.getCharno());
maybeSetLengthFrom(newBlock, node);
irNode = newBlock;
}
}
return irNode;
}
/**
* Check to see if the given block comment looks like it should be JSDoc.
*/
private void handleBlockComment(Comment comment) {
String value = comment.getValue();
if (value.indexOf("/* @") != -1 ||
value.indexOf("\n * @") != -1) {
errorReporter.warning(
SUSPICIOUS_COMMENT_WARNING,
sourceName,
comment.getLineno(), "", 0);
}
}
/**
* @return true if the jsDocParser represents a fileoverview.
*/
private boolean handlePossibleFileOverviewJsDoc(
JsDocInfoParser jsDocParser) {
if (jsDocParser.getFileOverviewJSDocInfo() != fileOverviewInfo) {
fileOverviewInfo = jsDocParser.getFileOverviewJSDocInfo();
return true;
}
return false;
}
private void handlePossibleFileOverviewJsDoc(Comment comment, Node irNode) {
JsDocInfoParser jsDocParser = createJsDocInfoParser(comment, irNode);
parsedComments.add(comment);
handlePossibleFileOverviewJsDoc(
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>
}
return node;
}
/**
* Transforms the given node and then sets its type to Token.STRING if it
* was Token.NAME. If its type was already Token.STRING, then quotes it.
* Used for properties, as the old AST uses String tokens, while the new one
* uses Name tokens for unquoted strings. For example, in
* var o = {'a' : 1, b: 2};
* the string 'a' is quoted, while the name b is turned into a string, but
* unquoted.
*/
private Node transformAsString(AstNode n) {
Node ret;
if (n instanceof Name) {
ret = transformNameAsString((Name)n);
} else if (n instanceof NumberLiteral) {
ret = transformNumberAsString((NumberLiteral)n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
} else {
ret = transform(n);
ret.putBooleanProp(Node.QUOTED_PROP, true);
}
Preconditions.checkState(ret.isString());
return ret;
}
@Override
Node processArrayLiteral(ArrayLiteral literalNode) {
if (literalNode.isDestructuring()) {
reportDestructuringAssign(literalNode);
}
Node node = newNode(Token.ARRAYLIT);
for (AstNode child : literalNode.getElements()) {
Node c = transform(child);
node.addChildToBack(c);
}
return node;
}
@Override
Node processAssignment(Assignment assignmentNode) {
Node assign = processInfixExpression(assignmentNode);
Node target = assign.getFirstChild();
if (!validAssignmentTarget(target)) {
errorReporter.error(
"invalid assignment target",
sourceName,
target.getLineno(), "", 0);
}
return assign;
}
@Override
Node processAstRoot(AstRoot rootNode) {
Node node = newNode(Token.SCRIPT);
for (com.google.javascript.rhino.head.Node child : rootNode) {
node.addChildToBack(transform((AstNode)child));
}
parseDirectives(node);
return node;
}
/**
* Parse the directives, encode them in the AST, and remove their nodes.
*
* For information on ES5 directives, see section 14.1 of
* ECMA-262, Edition 5.
*
* It would be nice if Rhino would eventually take care of
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> this for
* us, but right now their directive-processing is a one-off.
*/
private void parseDirectives(Node node) {
// Remove all the directives, and encode them in the AST.
Set<String> directives = null;
while (isDirective(node.getFirstChild())) {
String directive = node.removeFirstChild().getFirstChild().getString();
if (directives == null) {
directives = Sets.newHashSet(directive);
} else {
directives.add(directive);
}
}
if (directives != null) {
node.setDirectives(directives);
}
}
private boolean isDirective(Node n) {
if (n == null) return false;
int nType = n.getType();
return nType == Token.EXPR_RESULT &&
n.getFirstChild().isString() &&
ALLOWED_DIRECTIVES.contains(n.getFirstChild().getString());
}
@Override
Node processBlock(Block blockNode) {
return processGeneric(blockNode);
}
@Override
Node processBreakStatement(BreakStatement statementNode) {
Node node = newNode(Token.BREAK);
if (statementNode.getBreakLabel() != null) {
Node labelName = transform(statementNode.getBreakLabel());
// Change the NAME to LABEL_NAME
labelName.setType(Token.LABEL_NAME);
node.addChildToBack(labelName);
}
return node;
}
@Override
Node processCatchClause(CatchClause clauseNode) {
AstNode catchVar = clauseNode.getVarName();
Node node = newNode(Token.CATCH, transform(catchVar));
if (clauseNode.getCatchCondition() != null) {
errorReporter.error(
"Catch clauses are not supported",
sourceName,
clauseNode.getCatchCondition().getLineno(), "", 0);
}
node.addChildToBack(transformBlock(clauseNode.getBody()));
return node;
}
@Override
Node processConditionalExpression(ConditionalExpression exprNode) {
return newNode(
Token.HOOK,
transform(exprNode.getTestExpression()),
transform(exprNode.getTrueExpression()),
transform(exprNode.getFalseExpression()));
}
@Override
Node processContinueStatement(ContinueStatement statementNode) {
Node node = newNode(Token.CONTINUE);
if (statementNode.getLabel() != null) {
Node labelName = transform(statementNode.getLabel());
// Change the NAME to LABEL_NAME
labelName.setType(Token.LABEL_
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>NAME);
node.addChildToBack(labelName);
}
return node;
}
@Override
Node processDoLoop(DoLoop loopNode) {
return newNode(
Token.DO,
transformBlock(loopNode.getBody()),
transform(loopNode.getCondition()));
}
@Override
Node processElementGet(ElementGet getNode) {
return newNode(
Token.GETELEM,
transform(getNode.getTarget()),
transform(getNode.getElement()));
}
@Override
Node processEmptyExpression(EmptyExpression exprNode) {
Node node = newNode(Token.EMPTY);
return node;
}
@Override
Node processEmptyStatement(EmptyStatement exprNode) {
Node node = newNode(Token.EMPTY);
return node;
}
@Override
Node processExpressionStatement(ExpressionStatement statementNode) {
Node node = newNode(transformTokenType(statementNode.getType()));
node.addChildToBack(transform(statementNode.getExpression()));
return node;
}
@Override
Node processForInLoop(ForInLoop loopNode) {
if (loopNode.isForEach()) {
errorReporter.error(
"unsupported language extension: for each",
sourceName,
loopNode.getLineno(), "", 0);
// Return the bare minimum to put the AST in a valid state.
return newNode(Token.EXPR_RESULT, Node.newNumber(0));
}
return newNode(
Token.FOR,
transform(loopNode.getIterator()),
transform(loopNode.getIteratedObject()),
transformBlock(loopNode.getBody()));
}
@Override
Node processForLoop(ForLoop loopNode) {
Node node = newNode(
Token.FOR,
transform(loopNode.getInitializer()),
transform(loopNode.getCondition()),
transform(loopNode.getIncrement()));
node.addChildToBack(transformBlock(loopNode.getBody()));
return node;
}
@Override
Node processFunctionCall(FunctionCall callNode) {
Node node = newNode(transformTokenType(callNode.getType()),
transform(callNode.getTarget()));
for (AstNode child : callNode.getArguments()) {
node.addChildToBack(transform(child));
}
node.setLineno(node.getFirstChild().getLineno());
node.setCharno(node.getFirstChild().getCharno());
maybeSetLengthFrom(node, callNode);
return node;
}
@Override
Node processFunctionNode(FunctionNode functionNode) {
Name name
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> = functionNode.getFunctionName();
Boolean isUnnamedFunction = false;
if (name == null) {
int functionType = functionNode.getFunctionType();
if (functionType != FunctionNode.FUNCTION_EXPRESSION) {
errorReporter.error(
"unnamed function statement",
sourceName,
functionNode.getLineno(), "", 0);
// Return the bare minimum to put the AST in a valid state.
return newNode(Token.EXPR_RESULT, Node.newNumber(0));
}
name = new Name();
name.setIdentifier("");
isUnnamedFunction = true;
}
Node node = newNode(Token.FUNCTION);
Node newName = transform(name);
if (isUnnamedFunction) {
// Old Rhino tagged the empty name node with the line number of the
// declaration.
newName.setLineno(functionNode.getLineno());
// TODO(bowdidge) Mark line number of paren correctly.
// Same problem as below - the left paren might not be on the
// same line as the function keyword.
int lpColumn = functionNode.getAbsolutePosition() +
functionNode.getLp();
newName.setCharno(position2charno(lpColumn));
maybeSetLengthFrom(newName, name);
}
node.addChildToBack(newName);
Node lp = newNode(Token.PARAM_LIST);
// The left paren's complicated because it's not represented by an
// AstNode, so there's nothing that has the actual line number that it
// appeared on. We know the paren has to appear on the same line as the
// function name (or else a semicolon will be inserted.) If there's no
// function name, assume the paren was on the same line as the function.
// TODO(bowdidge): Mark line number of paren correctly.
Name fnName = functionNode.getFunctionName();
if (fnName != null) {
lp.setLineno(fnName.getLineno());
} else {
lp.setLineno(functionNode.getLineno());
}
int lparenCharno = functionNode.getLp() +
functionNode.getAbsolutePosition();
lp.setCharno(position2charno(lparenCharno));
for (AstNode param : functionNode.getParams()) {
Node paramNode = transform(param);
// When in ideMode Rhino can generate a param list with only a single
// ErrorNode. This is transformed into an EMPTY
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> node. Drop this node in
// ideMode to keep the AST in a valid state.
if (paramNode.isName()) {
lp.addChildToBack(paramNode);
} else {
// We expect this in ideMode or when there is an error handling
// destructuring parameter assignments which aren't supported
// (an error has already been reported).
Preconditions.checkState(
config.isIdeMode
|| paramNode.isObjectLit()
|| paramNode.isArrayLit());
}
}
node.addChildToBack(lp);
Node bodyNode = transform(functionNode.getBody());
if (!bodyNode.isBlock()) {
// When in ideMode Rhino tries to parse some constructs the compiler
// doesn't support, repair it here. see Rhino's
// Parser#parseFunctionBodyExpr.
Preconditions.checkState(config.isIdeMode);
bodyNode = IR.block();
}
parseDirectives(bodyNode);
node.addChildToBack(bodyNode);
return node;
}
@Override
Node processIfStatement(IfStatement statementNode) {
Node node = newNode(Token.IF);
node.addChildToBack(transform(statementNode.getCondition()));
node.addChildToBack(transformBlock(statementNode.getThenPart()));
if (statementNode.getElsePart() != null) {
node.addChildToBack(transformBlock(statementNode.getElsePart()));
}
return node;
}
@Override
Node processInfixExpression(InfixExpression exprNode) {
Node n = newNode(
transformTokenType(exprNode.getType()),
transform(exprNode.getLeft()),
transform(exprNode.getRight()));
n.setLineno(exprNode.getLineno());
n.setCharno(position2charno(exprNode.getAbsolutePosition()));
maybeSetLengthFrom(n, exprNode);
return n;
}
@Override
Node processKeywordLiteral(KeywordLiteral literalNode) {
return newNode(transformTokenType(literalNode.getType()));
}
@Override
Node processLabel(Label labelNode) {
return newStringNode(Token.LABEL_NAME, labelNode.getName());
}
@Override
Node processLabeledStatement(LabeledStatement statementNode) {
Node node = newNode(Token.LABEL);
Node prev = null;
Node cur = node;
for (Label label : statementNode.getLabels()) {
if (prev != null) {
prev.addChildToBack(cur);
}
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>
cur.addChildToBack(transform(label));
cur.setLineno(label.getLineno());
maybeSetLengthFrom(cur, label);
int clauseAbsolutePosition =
position2charno(label.getAbsolutePosition());
cur.setCharno(clauseAbsolutePosition);
prev = cur;
cur = newNode(Token.LABEL);
}
prev.addChildToBack(transform(statementNode.getStatement()));
return node;
}
@Override
Node processName(Name nameNode) {
return processName(nameNode, false);
}
Node processName(Name nameNode, boolean asString) {
if (asString) {
return newStringNode(Token.STRING, nameNode.getIdentifier());
} else {
if (isReservedKeyword(nameNode.getIdentifier())) {
errorReporter.error(
"identifier is a reserved word",
sourceName,
nameNode.getLineno(), "", 0);
}
return newStringNode(Token.NAME, nameNode.getIdentifier());
}
}
/**
* @return Whether the
*/
private boolean isReservedKeyword(String identifier) {
return reservedKeywords != null && reservedKeywords.contains(identifier);
}
@Override
Node processNewExpression(NewExpression exprNode) {
return processFunctionCall(exprNode);
}
@Override
Node processNumberLiteral(NumberLiteral literalNode) {
return newNumberNode(literalNode.getNumber());
}
@Override
Node processObjectLiteral(ObjectLiteral literalNode) {
if (literalNode.isDestructuring()) {
reportDestructuringAssign(literalNode);
}
Node node = newNode(Token.OBJECTLIT);
for (ObjectProperty el : literalNode.getElements()) {
if (config.languageMode == LanguageMode.ECMASCRIPT3) {
if (el.isGetter()) {
reportGetter(el);
continue;
} else if (el.isSetter()) {
reportSetter(el);
continue;
}
}
Node key = transformAsString(el.getLeft());
key.setType(Token.STRING_KEY);
Node value = transform(el.getRight());
if (el.isGetter()) {
key.setType(Token.GETTER_DEF);
Preconditions.checkState(value.isFunction());
if (getFnParamNode(value).hasChildren()) {
reportGetterParam(el.getLeft());
}
} else if (el.isSetter()) {
key.setType(Token.SETTER
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>_DEF);
Preconditions.checkState(value.isFunction());
if (!getFnParamNode(value).hasOneChild()) {
reportSetterParam(el.getLeft());
}
}
key.addChildToFront(value);
node.addChildToBack(key);
}
return node;
}
/**
* @param fnNode The function.
* @return The Node containing the Function parameters.
*/
Node getFnParamNode(Node fnNode) {
// Function NODE: [ FUNCTION -> NAME, LP -> ARG1, ARG2, ... ]
Preconditions.checkArgument(fnNode.isFunction());
return fnNode.getFirstChild().getNext();
}
@Override
Node processObjectProperty(ObjectProperty propertyNode) {
return processInfixExpression(propertyNode);
}
@Override
Node processParenthesizedExpression(ParenthesizedExpression exprNode) {
Node node = transform(exprNode.getExpression());
node.putProp(Node.PARENTHESIZED_PROP, Boolean.TRUE);
return node;
}
@Override
Node processPropertyGet(PropertyGet getNode) {
Node leftChild = transform(getNode.getTarget());
Node newNode = newNode(
Token.GETPROP, leftChild, transformAsString(getNode.getProperty()));
newNode.setLineno(leftChild.getLineno());
newNode.setCharno(leftChild.getCharno());
maybeSetLengthFrom(newNode, getNode);
return newNode;
}
@Override
Node processRegExpLiteral(RegExpLiteral literalNode) {
Node literalStringNode = newStringNode(literalNode.getValue());
// assume it's on the same line.
literalStringNode.setLineno(literalNode.getLineno());
maybeSetLengthFrom(literalStringNode, literalNode);
Node node = newNode(Token.REGEXP, literalStringNode);
String flags = literalNode.getFlags();
if (flags != null && !flags.isEmpty()) {
Node flagsNode = newStringNode(flags);
// Assume the flags are on the same line as the literal node.
flagsNode.setLineno(literalNode.getLineno());
maybeSetLengthFrom(flagsNode, literalNode);
node.addChildToBack(flagsNode);
}
return node;
}
@Override
Node processReturnStatement(ReturnStatement statementNode) {
Node node = newNode(Token.RETURN);
if (statementNode.getReturnValue() != null) {
node.addChildToBack(transform(statementNode.getReturnValue()));
}
return node
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>;
}
@Override
Node processScope(Scope scopeNode) {
return processGeneric(scopeNode);
}
@Override
Node processStringLiteral(StringLiteral literalNode) {
String value = literalNode.getValue();
Node n = newStringNode(value);
if (value.indexOf('\u000B') != -1) {
// NOTE(nicksantos): In JavaScript, there are 3 ways to
// represent a vertical tab: \v, \x0B, \u000B.
// The \v notation was added later, and is not understood
// on IE. So we need to preserve it as-is. This is really
// obnoxious, because we do not have a good way to represent
// how the original string was encoded without making the
// representation of strings much more complicated.
//
// To handle this, we look at the original source test, and
// mark the string as \v-encoded or not. If a string is
// \v encoded, then all the vertical tabs in that string
// will be encoded with a \v.
int start = literalNode.getAbsolutePosition();
int end = start + literalNode.getLength();
if (start < sourceString.length() &&
(sourceString.substring(
start, Math.min(sourceString.length(), end))
.indexOf("\\v") != -1)) {
n.putBooleanProp(Node.SLASH_V, true);
}
}
return n;
}
@Override
Node processSwitchCase(SwitchCase caseNode) {
Node node;
if (caseNode.isDefault()) {
node = newNode(Token.DEFAULT_CASE);
} else {
AstNode expr = caseNode.getExpression();
node = newNode(Token.CASE, transform(expr));
}
Node block = newNode(Token.BLOCK);
block.putBooleanProp(Node.SYNTHETIC_BLOCK_PROP, true);
block.setLineno(caseNode.getLineno());
block.setCharno(position2charno(caseNode.getAbsolutePosition()));
maybeSetLengthFrom(block, caseNode);
if (caseNode.getStatements() != null) {
for (AstNode child : caseNode.getStatements()) {
block.addChildToBack(transform(child));
}
}
node.addChildToBack(block);
return node;
}
@Override
Node processSwitchStatement(SwitchStatement statementNode) {
Node node
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> = newNode(Token.SWITCH,
transform(statementNode.getExpression()));
for (AstNode child : statementNode.getCases()) {
node.addChildToBack(transform(child));
}
return node;
}
@Override
Node processThrowStatement(ThrowStatement statementNode) {
return newNode(Token.THROW,
transform(statementNode.getExpression()));
}
@Override
Node processTryStatement(TryStatement statementNode) {
Node node = newNode(Token.TRY,
transformBlock(statementNode.getTryBlock()));
Node block = newNode(Token.BLOCK);
boolean lineSet = false;
for (CatchClause cc : statementNode.getCatchClauses()) {
// Mark the enclosing block at the same line as the first catch
// clause.
if (lineSet == false) {
block.setLineno(cc.getLineno());
maybeSetLengthFrom(block, cc);
lineSet = true;
}
block.addChildToBack(transform(cc));
}
node.addChildToBack(block);
AstNode finallyBlock = statementNode.getFinallyBlock();
if (finallyBlock != null) {
node.addChildToBack(transformBlock(finallyBlock));
}
// If we didn't set the line on the catch clause, then
// we've got an empty catch clause. Set its line to be the same
// as the finally block (to match Old Rhino's behavior.)
if ((lineSet == false) && (finallyBlock != null)) {
block.setLineno(finallyBlock.getLineno());
maybeSetLengthFrom(block, finallyBlock);
}
return node;
}
@Override
Node processUnaryExpression(UnaryExpression exprNode) {
int type = transformTokenType(exprNode.getType());
Node operand = transform(exprNode.getOperand());
if (type == Token.NEG && operand.isNumber()) {
operand.setDouble(-operand.getDouble());
return operand;
} else {
if (type == Token.DELPROP &&
!(operand.isGetProp() ||
operand.isGetElem() ||
operand.isName())) {
String msg =
"Invalid delete operand. Only properties can be deleted.";
errorReporter.error(
msg,
sourceName,
operand.getLineno(), "", 0);
} else if (type == Token.INC || type == Token.DEC) {
if (!validAssignmentTarget(operand)) {
String msg = (type == Token.INC)
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> ? "invalid increment target"
: "invalid decrement target";
errorReporter.error(
msg,
sourceName,
operand.getLineno(), "", 0);
}
}
Node node = newNode(type, operand);
if (exprNode.isPostfix()) {
node.putBooleanProp(Node.INCRDECR_PROP, true);
}
return node;
}
}
private boolean validAssignmentTarget(Node target) {
switch (target.getType()) {
case Token.NAME:
case Token.GETPROP:
case Token.GETELEM:
return true;
}
return false;
}
@Override
Node processVariableDeclaration(VariableDeclaration declarationNode) {
if (!config.acceptConstKeyword && declarationNode.getType() ==
com.google.javascript.rhino.head.Token.CONST) {
processIllegalToken(declarationNode);
}
Node node = newNode(Token.VAR);
for (VariableInitializer child : declarationNode.getVariables()) {
node.addChildToBack(transform(child));
}
return node;
}
@Override
Node processVariableInitializer(VariableInitializer initializerNode) {
Node node = transform(initializerNode.getTarget());
if (initializerNode.getInitializer() != null) {
Node initalizer = transform(initializerNode.getInitializer());
node.addChildToBack(initalizer);
}
return node;
}
@Override
Node processWhileLoop(WhileLoop loopNode) {
return newNode(
Token.WHILE,
transform(loopNode.getCondition()),
transformBlock(loopNode.getBody()));
}
@Override
Node processWithStatement(WithStatement statementNode) {
return newNode(
Token.WITH,
transform(statementNode.getExpression()),
transformBlock(statementNode.getStatement()));
}
@Override
Node processIllegalToken(AstNode node) {
errorReporter.error(
"Unsupported syntax: " +
com.google.javascript.rhino.head.Token.typeToName(
node.getType()),
sourceName,
node.getLineno(), "", 0);
return newNode(Token.EMPTY);
}
void reportDestructuringAssign(AstNode node) {
errorReporter.error(
"destructuring assignment forbidden",
sourceName,
node.getLineno(), "", 0);
}
void reportGetter(AstNode node) {
errorReporter.error(
GETTER_ERROR_MESSAGE,
sourceName,
node.get
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>Lineno(), "", 0);
}
void reportSetter(AstNode node) {
errorReporter.error(
SETTER_ERROR_MESSAGE,
sourceName,
node.getLineno(), "", 0);
}
void reportGetterParam(AstNode node) {
errorReporter.error(
"getters may not have parameters",
sourceName,
node.getLineno(), "", 0);
}
void reportSetterParam(AstNode node) {
errorReporter.error(
"setters must have exactly one parameter",
sourceName,
node.getLineno(), "", 0);
}
}
private static int transformTokenType(int token) {
switch (token) {
case com.google.javascript.rhino.head.Token.RETURN:
return Token.RETURN;
case com.google.javascript.rhino.head.Token.BITOR:
return Token.BITOR;
case com.google.javascript.rhino.head.Token.BITXOR:
return Token.BITXOR;
case com.google.javascript.rhino.head.Token.BITAND:
return Token.BITAND;
case com.google.javascript.rhino.head.Token.EQ:
return Token.EQ;
case com.google.javascript.rhino.head.Token.NE:
return Token.NE;
case com.google.javascript.rhino.head.Token.LT:
return Token.LT;
case com.google.javascript.rhino.head.Token.LE:
return Token.LE;
case com.google.javascript.rhino.head.Token.GT:
return Token.GT;
case com.google.javascript.rhino.head.Token.GE:
return Token.GE;
case com.google.javascript.rhino.head.Token.LSH:
return Token.LSH;
case com.google.javascript.rhino.head.Token.RSH:
return Token.RSH;
case com.google.javascript.rhino.head.Token.URSH:
return Token.URSH;
case com.google.javascript.rhino.head.Token.ADD:
return Token.ADD;
case com.google.javascript.rhino.head.Token.SUB:
return Token.SUB;
case com.google.javascript.rhino.head.Token.MUL:
return Token.MUL;
case com.google.javascript.rhino.head.Token.DIV:
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>
return Token.DIV;
case com.google.javascript.rhino.head.Token.MOD:
return Token.MOD;
case com.google.javascript.rhino.head.Token.NOT:
return Token.NOT;
case com.google.javascript.rhino.head.Token.BITNOT:
return Token.BITNOT;
case com.google.javascript.rhino.head.Token.POS:
return Token.POS;
case com.google.javascript.rhino.head.Token.NEG:
return Token.NEG;
case com.google.javascript.rhino.head.Token.NEW:
return Token.NEW;
case com.google.javascript.rhino.head.Token.DELPROP:
return Token.DELPROP;
case com.google.javascript.rhino.head.Token.TYPEOF:
return Token.TYPEOF;
case com.google.javascript.rhino.head.Token.GETPROP:
return Token.GETPROP;
case com.google.javascript.rhino.head.Token.GETELEM:
return Token.GETELEM;
case com.google.javascript.rhino.head.Token.CALL:
return Token.CALL;
case com.google.javascript.rhino.head.Token.NAME:
return Token.NAME;
case com.google.javascript.rhino.head.Token.NUMBER:
return Token.NUMBER;
case com.google.javascript.rhino.head.Token.STRING:
return Token.STRING;
case com.google.javascript.rhino.head.Token.NULL:
return Token.NULL;
case com.google.javascript.rhino.head.Token.THIS:
return Token.THIS;
case com.google.javascript.rhino.head.Token.FALSE:
return Token.FALSE;
case com.google.javascript.rhino.head.Token.TRUE:
return Token.TRUE;
case com.google.javascript.rhino.head.Token.SHEQ:
return Token.SHEQ;
case com.google.javascript.rhino.head.Token.SHNE:
return Token.SHNE;
case com.google.javascript.rhino.head.Token.REGEXP:
return Token.REGEXP;
case com.google.javascript.rhino.head.Token.THROW:
return Token.THROW;
case com.google.javascript.rhino.head.Token.IN:
return Token.IN;
case
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> com.google.javascript.rhino.head.Token.INSTANCEOF:
return Token.INSTANCEOF;
case com.google.javascript.rhino.head.Token.ARRAYLIT:
return Token.ARRAYLIT;
case com.google.javascript.rhino.head.Token.OBJECTLIT:
return Token.OBJECTLIT;
case com.google.javascript.rhino.head.Token.TRY:
return Token.TRY;
// The LP represents a parameter list
case com.google.javascript.rhino.head.Token.LP:
return Token.PARAM_LIST;
case com.google.javascript.rhino.head.Token.COMMA:
return Token.COMMA;
case com.google.javascript.rhino.head.Token.ASSIGN:
return Token.ASSIGN;
case com.google.javascript.rhino.head.Token.ASSIGN_BITOR:
return Token.ASSIGN_BITOR;
case com.google.javascript.rhino.head.Token.ASSIGN_BITXOR:
return Token.ASSIGN_BITXOR;
case com.google.javascript.rhino.head.Token.ASSIGN_BITAND:
return Token.ASSIGN_BITAND;
case com.google.javascript.rhino.head.Token.ASSIGN_LSH:
return Token.ASSIGN_LSH;
case com.google.javascript.rhino.head.Token.ASSIGN_RSH:
return Token.ASSIGN_RSH;
case com.google.javascript.rhino.head.Token.ASSIGN_URSH:
return Token.ASSIGN_URSH;
case com.google.javascript.rhino.head.Token.ASSIGN_ADD:
return Token.ASSIGN_ADD;
case com.google.javascript.rhino.head.Token.ASSIGN_SUB:
return Token.ASSIGN_SUB;
case com.google.javascript.rhino.head.Token.ASSIGN_MUL:
return Token.ASSIGN_MUL;
case com.google.javascript.rhino.head.Token.ASSIGN_DIV:
return Token.ASSIGN_DIV;
case com.google.javascript.rhino.head.Token.ASSIGN_MOD:
return Token.ASSIGN_MOD;
case com.google.javascript.rhino.head.Token.HOOK:
return Token.HOOK;
case com.google.javascript.rhino.head.Token.OR:
return Token.OR;
case com.google.javascript.rhino.head.Token.AND:
return Token.AND;
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>
case com.google.javascript.rhino.head.Token.INC:
return Token.INC;
case com.google.javascript.rhino.head.Token.DEC:
return Token.DEC;
case com.google.javascript.rhino.head.Token.FUNCTION:
return Token.FUNCTION;
case com.google.javascript.rhino.head.Token.IF:
return Token.IF;
case com.google.javascript.rhino.head.Token.SWITCH:
return Token.SWITCH;
case com.google.javascript.rhino.head.Token.CASE:
return Token.CASE;
case com.google.javascript.rhino.head.Token.DEFAULT:
return Token.DEFAULT_CASE;
case com.google.javascript.rhino.head.Token.WHILE:
return Token.WHILE;
case com.google.javascript.rhino.head.Token.DO:
return Token.DO;
case com.google.javascript.rhino.head.Token.FOR:
return Token.FOR;
case com.google.javascript.rhino.head.Token.BREAK:
return Token.BREAK;
case com.google.javascript.rhino.head.Token.CONTINUE:
return Token.CONTINUE;
case com.google.javascript.rhino.head.Token.VAR:
return Token.VAR;
case com.google.javascript.rhino.head.Token.WITH:
return Token.WITH;
case com.google.javascript.rhino.head.Token.CATCH:
return Token.CATCH;
case com.google.javascript.rhino.head.Token.VOID:
return Token.VOID;
case com.google.javascript.rhino.head.Token.EMPTY:
return Token.EMPTY;
case com.google.javascript.rhino.head.Token.BLOCK:
return Token.BLOCK;
case com.google.javascript.rhino.head.Token.LABEL:
return Token.LABEL;
case com.google.javascript.rhino.head.Token.EXPR_VOID:
case com.google.javascript.rhino.head.Token.EXPR_RESULT:
return Token.EXPR_RESULT;
case com.google.javascript.rhino.head.Token.SCRIPT:
return Token.SCRIPT;
case com.google.javascript.rhino.head.Token.GET:
return Token.GETTER_DEF;
case com.google.javascript.rhino.head.Token.SET:
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>
return Token.SETTER_DEF;
case com.google.javascript.rhino.head.Token.CONST:
return Token.CONST;
case com.google.javascript.rhino.head.Token.DEBUGGER:
return Token.DEBUGGER;
}
// Token without name
throw new IllegalStateException(String.valueOf(token));
}
// Simple helper to create nodes and set the initial node properties.
private Node newNode(int type) {
return new Node(type).clonePropsFrom(templateNode);
}
private Node newNode(int type, Node child1) {
return new Node(type, child1).clonePropsFrom(templateNode);
}
private Node newNode(int type, Node child1, Node child2) {
return new Node(type, child1, child2).clonePropsFrom(templateNode);
}
private Node newNode(int type, Node child1, Node child2, Node child3) {
return new Node(type, child1, child2, child3).clonePropsFrom(templateNode);
}
private Node newStringNode(String value) {
return IR.string(value).clonePropsFrom(templateNode);
}
private Node newStringNode(int type, String value) {
return Node.newString(type, value).clonePropsFrom(templateNode);
}
private Node newNumberNode(Double value) {
return IR.number(value).clonePropsFrom(templateNode);
}
}
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> {
if (synthesizedExternsInput == null) {
synthesizedExternsInput = newExternInput(SYNTHETIC_EXTERNS);
}
return synthesizedExternsInput;
}
@Override
public double getProgress() {
return progress;
}
@Override
void setProgress(double newProgress) {
if (newProgress > 1.0) {
progress = 1.0;
} else if (newProgress < 0.0) {
progress = 0.0;
} else {
progress = newProgress;
}
}
/**
* Replaces one file in a hot-swap mode. The given JsAst should be made
* from a new version of a file that already was present in the last compile
* call. If the file is new, this will silently ignored.
*
* @param ast the ast of the file that is being replaced
*/
public void replaceScript(JsAst ast) {
CompilerInput input = this.getInput(ast.getInputId());
if (!replaceIncrementalSourceAst(ast)) {
return;
}
Node originalRoot = input.getAstRoot(this);
processNewScript(ast, originalRoot);
}
/**
* Adds a new Script AST to the compile state. If a script for the same file
* already exists the script will not be added, instead a call to
* #replaceScript should be used.
*
* @param ast the ast of the new file
*/
public void addNewScript(JsAst ast) {
if (!addNewSourceAst(ast)) {
return;
}
Node emptyScript = new Node(Token.SCRIPT);
InputId inputId = ast.getInputId();
emptyScript.setInputId(inputId);
emptyScript.setStaticSourceFile(
SourceFile.fromCode(inputId.getIdName(), ""));
processNewScript(ast, emptyScript);
}
private void processNewScript(JsAst ast, Node originalRoot) {
Node js = ast.getAstRoot(this);
Preconditions.checkNotNull(js);
runHotSwap(originalRoot, js, this.getCleanupPassConfig());
// NOTE: If hot swap passes that use GlobalNamespace are added, we will need
// to revisit this approach to clearing GlobalNamespaces
runHotSwapPass(null, null, ensureDefaultPassConfig().garbageCollectChecks);
this.getTypeRegistry().clearNamedTypes();
this.removeSyntheticVarsInput();
runHotSwap(originalRoot,
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>/*
* Copyright 2011 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.rhino.InputId;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
/**
* This class walks the AST and validates that the structure is correct.
*
* @author johnlenz@google.com (John Lenz)
*/
public class AstValidator implements CompilerPass {
// Possible enhancements:
// * verify NAME, LABEL_NAME, GETPROP property name and unquoted
// object-literal keys are valid JavaScript identifiers.
// * optionally verify every node has source location information.
// * optionally verify every node has an assigned JSType
//
public interface ViolationHandler {
void handleViolation(String message, Node n);
}
private final ViolationHandler violationHandler;
public AstValidator(ViolationHandler handler) {
this.violationHandler = handler;
}
public AstValidator() {
this.violationHandler = new ViolationHandler() {
@Override
public void handleViolation(String message, Node n) {
throw new IllegalStateException(
message + " Reference node " + n.toString());
}
};
}
@Override
public void process(Node externs, Node root) {
if (externs != null) {
validateCodeRoot(externs);
}
if (root != null) {
validateCodeRoot(root);
}
}
public void validateRoot(Node n) {
validateNodeType(Token.BLOCK, n);
validateIsSynthetic(n);
validateChildCount(n, 2);
validateCodeRoot(n.getFirstChild());
validateCodeRoot(n.getLastChild());
}
public void validateCodeRoot(Node n) {
validateNodeType(Token.BLOCK, n);
validateIsSynthetic
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>(n);
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
validateScript(c);
}
}
public void validateScript(Node n) {
validateNodeType(Token.SCRIPT, n);
validateIsSynthetic(n);
validateHasSourceName(n);
validateHasInputId(n);
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
validateStatement(c);
}
}
public void validateStatement(Node n) {
switch (n.getType()) {
case Token.LABEL:
validateLabel(n);
return;
case Token.BLOCK:
validateBlock(n);
return;
case Token.FUNCTION:
validateFunctionStatement(n);
return;
case Token.WITH:
validateWith(n);
return;
case Token.FOR:
validateFor(n);
return;
case Token.WHILE:
validateWhile(n);
return;
case Token.DO:
validateDo(n);
return;
case Token.SWITCH:
validateSwitch(n);
return;
case Token.IF:
validateIf(n);
return;
case Token.VAR:
validateVar(n);
return;
case Token.EXPR_RESULT:
validateExprStmt(n);
return;
case Token.RETURN:
validateReturn(n);
return;
case Token.THROW:
validateThrow(n);
return;
case Token.TRY:
validateTry(n);
return;
case Token.BREAK:
validateBreak(n);
return;
case Token.CONTINUE:
validateContinue(n);
return;
case Token.EMPTY:
validateChildless(n);
return;
case Token.DEBUGGER:
validateChildless(n);
return;
default:
violation("Expected statement but was "
+ Token.name(n.getType()) + ".", n);
}
}
public void validateExpression(Node n) {
switch (n.getType()) {
// Childless expressions
case Token.FALSE:
case Token.NULL:
case Token.THIS:
case Token.TRUE:
validateChildless(n);
return;
// General unary ops
case Token.DELPROP:
case Token.POS:
case Token.NEG:
case Token.NOT:
case Token.INC:
case Token.DEC:
case Token.TYPEOF:
case Token.VOID:
case Token.BIT
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>NOT:
validateUnaryOp(n);
return;
// General binary ops
case Token.COMMA:
case Token.OR:
case Token.AND:
case Token.BITOR:
case Token.BITXOR:
case Token.BITAND:
case Token.EQ:
case Token.NE:
case Token.SHEQ:
case Token.SHNE:
case Token.LT:
case Token.GT:
case Token.LE:
case Token.GE:
case Token.INSTANCEOF:
case Token.IN:
case Token.LSH:
case Token.RSH:
case Token.URSH:
case Token.SUB:
case Token.ADD:
case Token.MUL:
case Token.MOD:
case Token.DIV:
validateBinaryOp(n);
return;
// Assignments
case Token.ASSIGN:
case Token.ASSIGN_BITOR:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_BITAND:
case Token.ASSIGN_LSH:
case Token.ASSIGN_RSH:
case Token.ASSIGN_URSH:
case Token.ASSIGN_ADD:
case Token.ASSIGN_SUB:
case Token.ASSIGN_MUL:
case Token.ASSIGN_DIV:
case Token.ASSIGN_MOD:
validateAssignmentExpression(n);
return;
case Token.HOOK:
validateTrinaryOp(n);
return;
// Node types that require special handling
case Token.STRING:
validateString(n);
return;
case Token.NUMBER:
validateNumber(n);
return;
case Token.NAME:
validateName(n);
return;
case Token.GETELEM:
validateBinaryOp(n);
return;
case Token.GETPROP:
validateGetProp(n);
return;
case Token.ARRAYLIT:
validateArrayLit(n);
return;
case Token.OBJECTLIT:
validateObjectLit(n);
return;
case Token.REGEXP:
validateRegExpLit(n);
return;
case Token.CALL:
validateCall(n);
return;
case Token.NEW:
validateNew(n);
return;
case Token.FUNCTION:
validateFunctionExpression(n);
return;
default:
violation("Expected expression but was "
+ Token.name(n.getType()), n);
}
}
private void validateBlock(Node n) {
validateNodeType(Token.BLOCK, n);
for (Node c = n.getFirstChild();
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> c != null; c = c.getNext()) {
validateStatement(c);
}
}
private void validateSyntheticBlock(Node n) {
validateNodeType(Token.BLOCK, n);
validateIsSynthetic(n);
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
validateStatement(c);
}
}
private void validateIsSynthetic(Node n) {
if (!n.getBooleanProp(Node.SYNTHETIC_BLOCK_PROP)) {
violation("Missing 'synthetic block' annotation.", n);
}
}
private void validateHasSourceName(Node n) {
String sourceName = n.getSourceFileName();
if (sourceName == null || sourceName.isEmpty()) {
violation("Missing 'source name' annotation.", n);
}
}
private void validateHasInputId(Node n) {
InputId inputId = n.getInputId();
if (inputId == null) {
violation("Missing 'input id' annotation.", n);
}
}
private void validateLabel(Node n) {
validateNodeType(Token.LABEL, n);
validateChildCount(n, 2);
validateLabelName(n.getFirstChild());
validateStatement(n.getLastChild());
}
private void validateLabelName(Node n) {
validateNodeType(Token.LABEL_NAME, n);
validateNonEmptyString(n);
validateChildCount(n, 0);
}
private void validateNonEmptyString(Node n) {
validateNonNullString(n);
if (n.getString().isEmpty()) {
violation("Expected non-empty string.", n);
}
}
private void validateNonNullString(Node n) {
if (n.getString() == null) {
violation("Expected non-null string.", n);
}
}
private void validateName(Node n) {
validateNodeType(Token.NAME, n);
validateNonEmptyString(n);
validateChildCount(n, 0);
}
private void validateOptionalName(Node n) {
validateNodeType(Token.NAME, n);
validateNonNullString(n);
validateChildCount(n, 0);
}
private void validateFunctionStatement(Node n) {
validateNodeType(Token.FUNCTION, n);
validateChildCount(n, 3);
validateName(n.getFirstChild());
validateParameters(n.getChildAtIndex(1));
validateBlock(n.getLastChild());
}
private void validateFunctionExpression(
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>Node n) {
validateNodeType(Token.FUNCTION, n);
validateChildCount(n, 3);
validateOptionalName(n.getFirstChild());
validateParameters(n.getChildAtIndex(1));
validateBlock(n.getLastChild());
}
private void validateParameters(Node n) {
validateNodeType(Token.PARAM_LIST, n);
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
validateName(c);
}
}
private void validateCall(Node n) {
validateNodeType(Token.CALL, n);
validateMinimumChildCount(n, 1);
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
validateExpression(c);
}
}
private void validateNew(Node n) {
validateNodeType(Token.NEW, n);
validateMinimumChildCount(n, 1);
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
validateExpression(c);
}
}
private void validateVar(Node n) {
validateNodeType(Token.VAR, n);
this.validateMinimumChildCount(n, 1);
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
// Don't use the validateName here as the NAME is allowed to have
// a child.
validateNodeType(Token.NAME, c);
validateNonEmptyString(c);
validateMaximumChildCount(c, 1);
if (c.hasChildren()) {
validateExpression(c.getFirstChild());
}
}
}
private void validateFor(Node n) {
validateNodeType(Token.FOR, n);
validateMinimumChildCount(n, 3);
validateMaximumChildCount(n, 4);
if (NodeUtil.isForIn(n)) {
// FOR-IN
validateChildCount(n, 3);
validateVarOrAssignmentTarget(n.getFirstChild());
validateExpression(n.getChildAtIndex(1));
} else {
// FOR
validateChildCount(n, 4);
validateVarOrOptionalExpression(n.getFirstChild());
validateOptionalExpression(n.getChildAtIndex(1));
validateOptionalExpression(n.getChildAtIndex(2));
}
validateBlock(n.getLastChild());
}
private void validateVarOrOptionalExpression(Node n) {
if (n.isVar()) {
validateVar(n);
}
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> else {
validateOptionalExpression(n);
}
}
private void validateVarOrAssignmentTarget(Node n) {
if (n.isVar()) {
// Only one NAME can be declared for FOR-IN expressions.
this.validateChildCount(n, 1);
validateVar(n);
} else {
validateAssignmentTarget(n);
}
}
private void validateWith(Node n) {
validateNodeType(Token.WITH, n);
validateChildCount(n, 2);
validateExpression(n.getFirstChild());
validateBlock(n.getLastChild());
}
private void validateWhile(Node n) {
validateNodeType(Token.WHILE, n);
validateChildCount(n, 2);
validateExpression(n.getFirstChild());
validateBlock(n.getLastChild());
}
private void validateDo(Node n) {
validateNodeType(Token.DO, n);
validateChildCount(n, 2);
validateBlock(n.getFirstChild());
validateExpression(n.getLastChild());
}
private void validateIf(Node n) {
validateNodeType(Token.IF, n);
validateMinimumChildCount(n, 2);
validateMaximumChildCount(n, 3);
validateExpression(n.getFirstChild());
validateBlock(n.getChildAtIndex(1));
if (n.getChildCount() == 3) {
validateBlock(n.getLastChild());
}
}
private void validateExprStmt(Node n) {
validateNodeType(Token.EXPR_RESULT, n);
validateChildCount(n, 1);
validateExpression(n.getFirstChild());
}
private void validateReturn(Node n) {
validateNodeType(Token.RETURN, n);
validateMaximumChildCount(n, 1);
if (n.hasChildren()) {
validateExpression(n.getFirstChild());
}
}
private void validateThrow(Node n) {
validateNodeType(Token.THROW, n);
validateChildCount(n, 1);
validateExpression(n.getFirstChild());
}
private void validateBreak(Node n) {
validateNodeType(Token.BREAK, n);
validateMaximumChildCount(n, 1);
if (n.hasChildren()) {
validateLabelName(n.getFirstChild());
}
}
private void validateContinue(Node n) {
validateNodeType(Token.CONTINUE, n);
validateMaximumChildCount(n, 1);
if (n.hasChildren()) {
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>
validateLabelName(n.getFirstChild());
}
}
private void validateTry(Node n) {
validateNodeType(Token.TRY, n);
validateMinimumChildCount(n, 2);
validateMaximumChildCount(n, 3);
validateBlock(n.getFirstChild());
boolean seenCatchOrFinally = false;
// Validate catch
Node catches = n.getChildAtIndex(1);
validateNodeType(Token.BLOCK, catches);
validateMaximumChildCount(catches, 1);
if (catches.hasChildren()) {
validateCatch(catches.getFirstChild());
seenCatchOrFinally = true;
}
// Validate finally
if (n.getChildCount() == 3) {
validateBlock(n.getLastChild());
seenCatchOrFinally = true;
}
if (!seenCatchOrFinally) {
violation("Missing catch or finally for try statement.", n);
}
}
private void validateCatch(Node n) {
validateNodeType(Token.CATCH, n);
validateChildCount(n, 2);
validateName(n.getFirstChild());
validateBlock(n.getLastChild());
}
private void validateSwitch(Node n) {
validateNodeType(Token.SWITCH, n);
validateMinimumChildCount(n, 1);
validateExpression(n.getFirstChild());
int defaults = 0;
for (Node c = n.getFirstChild().getNext(); c != null; c = c.getNext()) {
validateSwitchMember(n.getLastChild());
if (c.isDefaultCase()) {
defaults++;
}
}
if (defaults > 1) {
violation("Expected at most 1 'default' in switch but was "
+ defaults, n);
}
}
private void validateSwitchMember(Node n) {
switch (n.getType()) {
case Token.CASE:
validateCase(n);
return;
case Token.DEFAULT_CASE:
validateDefault(n);
return;
default:
violation("Expected switch member but was "
+ Token.name(n.getType()), n);
}
}
private void validateDefault(Node n) {
validateNodeType(Token.DEFAULT_CASE, n);
validateChildCount(n, 1);
validateSyntheticBlock(n.getLastChild());
}
private void validateCase(Node n) {
validateNodeType(Token.CASE, n);
validateChildCount(n, 2);
validateExpression(n.getFirstChild());
validate
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>SyntheticBlock(n.getLastChild());
}
private void validateOptionalExpression(Node n) {
if (n.isEmpty()) {
validateChildless(n);
} else {
validateExpression(n);
}
}
private void validateChildless(Node n) {
validateChildCount(n, 0);
}
private void validateAssignmentExpression(Node n) {
validateChildCount(n, 2);
validateAssignmentTarget(n.getFirstChild());
validateExpression(n.getLastChild());
}
private void validateAssignmentTarget(Node n) {
switch (n.getType()) {
case Token.NAME:
case Token.GETELEM:
case Token.GETPROP:
validateExpression(n);
return;
default:
violation("Expected assignment target expression but was "
+ Token.name(n.getType()), n);
}
}
private void validateGetProp(Node n) {
validateNodeType(Token.GETPROP, n);
validateChildCount(n, 2);
validateExpression(n.getFirstChild());
Node prop = n.getLastChild();
validateNodeType(Token.STRING, prop);
validateNonEmptyString(prop);
}
private void validateRegExpLit(Node n) {
validateNodeType(Token.REGEXP, n);
validateMinimumChildCount(n, 1);
validateMaximumChildCount(n, 2);
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
validateString(c);
}
}
private void validateString(Node n) {
validateNodeType(Token.STRING, n);
validateChildCount(n, 0);
try {
// Validate that getString doesn't throw
n.getString();
} catch (UnsupportedOperationException e) {
violation("Invalid STRING node.", n);
}
}
private void validateNumber(Node n) {
validateNodeType(Token.NUMBER, n);
validateChildCount(n, 0);
try {
// Validate that getDouble doesn't throw
n.getDouble();
} catch (UnsupportedOperationException e) {
violation("Invalid NUMBER node.", n);
}
}
private void validateArrayLit(Node n) {
validateNodeType(Token.ARRAYLIT, n);
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
// EMPTY is allowed to represent empty slots.
validateOptionalExpression(c);
}
}
private void validateObjectLit(Node n) {
validate
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>NodeType(Token.OBJECTLIT, n);
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
validateObjectLitKey(c);
}
}
private void validateObjectLitKey(Node n) {
switch (n.getType()) {
case Token.GETTER_DEF:
validateObjectLitGetKey(n);
return;
case Token.SETTER_DEF:
validateObjectLitSetKey(n);
return;
case Token.STRING_KEY:
validateObjectLitStringKey(n);
return;
default:
violation("Expected object literal key expression but was "
+ Token.name(n.getType()), n);
}
}
private void validateObjectLitGetKey(Node n) {
validateNodeType(Token.GETTER_DEF, n);
validateChildCount(n, 1);
validateObjectLiteralKeyName(n);
Node function = n.getFirstChild();
validateFunctionExpression(function);
// objlit get functions must be nameless, and must have zero parameters.
if (!function.getFirstChild().getString().isEmpty()) {
violation("Expected unnamed function expression.", n);
}
Node functionParams = function.getChildAtIndex(1);
if (functionParams.hasChildren()) {
violation("get methods must not have parameters.", n);
}
}
private void validateObjectLitSetKey(Node n) {
validateNodeType(Token.SETTER_DEF, n);
validateChildCount(n, 1);
validateObjectLiteralKeyName(n);
Node function = n.getFirstChild();
validateFunctionExpression(function);
// objlit set functions must be nameless, and must have 1 parameter.
if (!function.getFirstChild().getString().isEmpty()) {
violation("Expected unnamed function expression.", n);
}
Node functionParams = function.getChildAtIndex(1);
if (!functionParams.hasOneChild()) {
violation("set methods must have exactly one parameter.", n);
}
}
private void validateObjectLitStringKey(Node n) {
validateNodeType(Token.STRING_KEY, n);
validateChildCount(n, 1);
validateObjectLiteralKeyName(n);
validateExpression(n.getFirstChild());
}
private void validateObjectLiteralKeyName(Node n) {
if (n.isQuotedString()) {
try {
// Validate that getString doesn't throw
n.getString();
} catch (UnsupportedOperationException e) {
violation("getString failed for" + Token.name(n.getType
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>()), n);
}
} else {
validateNonEmptyString(n);
}
}
private void validateUnaryOp(Node n) {
validateChildCount(n, 1);
validateExpression(n.getFirstChild());
}
private void validateBinaryOp(Node n) {
validateChildCount(n, 2);
validateExpression(n.getFirstChild());
validateExpression(n.getLastChild());
}
private void validateTrinaryOp(Node n) {
validateChildCount(n, 3);
Node first = n.getFirstChild();
validateExpression(first);
validateExpression(first.getNext());
validateExpression(n.getLastChild());
}
private void violation(String message, Node n) {
violationHandler.handleViolation(message, n);
}
private void validateNodeType(int type, Node n) {
if (n.getType() != type) {
violation(
"Expected " + Token.name(type) + " but was "
+ Token.name(n.getType()), n);
}
}
private void validateChildCount(Node n, int i) {
boolean valid = false;
if (i == 0) {
valid = !n.hasChildren();
} else if (i == 1) {
valid = n.hasOneChild();
} else {
valid = (n.getChildCount() == i);
}
if (!valid) {
violation(
"Expected " + i + " children, but was "
+ n.getChildCount(), n);
}
}
private void validateMinimumChildCount(Node n, int i) {
boolean valid = false;
if (i == 1) {
valid = n.hasChildren();
} else if (i == 2) {
valid = n.hasMoreThanOneChild();
} else {
valid = n.getChildCount() >= i;
}
if (!valid) {
violation(
"Expected at least " + i + " children, but was "
+ n.getChildCount(), n);
}
}
private void validateMaximumChildCount(Node n, int i) {
boolean valid = false;
if (i == 1) {
valid = !n.hasMoreThanOneChild();
} else {
valid = n.getChildCount() <= i;
}
if (!valid) {
violation(
"Expected no more than " + i + " children, but was "
+ n.getChildCount(), n);
}
}
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> parent) {
inputId = null;
if (parent == null) {
scope = new Scope(n, compiler);
} else {
scope = new Scope(parent, n);
}
scanRoot(n, parent);
inputId = null;
Scope returnedScope = scope;
scope = null;
return returnedScope;
}
private void scanRoot(Node n, Scope parent) {
if (n.isFunction()) {
if (inputId == null) {
inputId = NodeUtil.getInputId(n);
// TODO(johnlenz): inputId maybe null if the FUNCTION node is detached
// from the AST.
// Is it meaningful to build a scope for detached FUNCTION node?
}
final Node fnNameNode = n.getFirstChild();
final Node args = fnNameNode.getNext();
final Node body = args.getNext();
// Bleed the function name into the scope, if it hasn't
// been declared in the outer scope.
String fnName = fnNameNode.getString();
if (!fnName.isEmpty() && NodeUtil.isFunctionExpression(n)) {
declareVar(fnNameNode);
}
// Args: Declare function variables
Preconditions.checkState(args.isParamList());
for (Node a = args.getFirstChild(); a != null;
a = a.getNext()) {
Preconditions.checkState(a.isName());
declareVar(a);
}
// Body
scanVars(body, n);
} else {
// It's the global block
Preconditions.checkState(scope.getParent() == null);
scanVars(n, null);
}
}
/**
* Scans and gather variables declarations under a Node
*/
private void scanVars(Node n, Node parent) {
switch (n.getType()) {
case Token.VAR:
// Declare all variables. e.g. var x = 1, y, z;
for (Node child = n.getFirstChild();
child != null;) {
Node next = child.getNext();
declareVar(child);
child = next;
}
return;
case Token.FUNCTION:
if (NodeUtil.isFunctionExpression(n)) {
return;
}
String fnName = n.getFirstChild().getString();
if (fnName.isEmpty()) {
// This is invalid, but allow it so the checks can catch it.
return;
}
declareVar(n.getFirstChild());
return; // should not examine function's children
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> case Token.CATCH:
Preconditions.checkState(n.getChildCount() == 2);
Preconditions.checkState(n.getFirstChild().isName());
// the first child is the catch var and the third child
// is the code block
final Node var = n.getFirstChild();
final Node block = var.getNext();
declareVar(var);
scanVars(block, n);
return; // only one child to scan
case Token.SCRIPT:
inputId = n.getInputId();
Preconditions.checkNotNull(inputId);
break;
}
// Variables can only occur in statement-level nodes, so
// we only need to traverse children in a couple special cases.
if (NodeUtil.isControlStructure(n) || NodeUtil.isStatementBlock(n)) {
for (Node child = n.getFirstChild();
child != null;) {
Node next = child.getNext();
scanVars(child, n);
child = next;
}
}
}
/**
* Interface for injectable duplicate handling.
*/
interface RedeclarationHandler {
void onRedeclaration(
Scope s, String name, Node n, CompilerInput input);
}
/**
* The default handler for duplicate declarations.
*/
private class DefaultRedeclarationHandler implements RedeclarationHandler {
@Override
public void onRedeclaration(
Scope s, String name, Node n, CompilerInput input) {
Node parent = n.getParent();
// Don't allow multiple variables to be declared at the top-level scope
if (scope.isGlobal()) {
Scope.Var origVar = scope.getVar(name);
Node origParent = origVar.getParentNode();
if (origParent.isCatch() &&
parent.isCatch()) {
// Okay, both are 'catch(x)' variables.
return;
}
boolean allowDupe = hasDuplicateDeclarationSuppression(n, origVar);
if (!allowDupe) {
compiler.report(
JSError.make(NodeUtil.getSourceName(n), n,
VAR_MULTIPLY_DECLARED_ERROR,
name,
(origVar.input != null
? origVar.input.getName()
: "??")));
}
} else if (name.equals(ARGUMENTS) && !NodeUtil.isVarDeclaration(n)) {
// Disallow shadowing "arguments" as we can't handle with our current
// scope modeling.
compiler.report(
JSError.make(NodeUtil.getSourceName(n), n,
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> final DiagnosticType ARGUMENTS_ASSIGNMENT = DiagnosticType.warning(
"JSC_ARGUMENTS_ASSIGNMENT",
"the \"arguments\" object cannot be reassigned in ES5 strict mode");
static final DiagnosticType DELETE_VARIABLE = DiagnosticType.warning(
"JSC_DELETE_VARIABLE",
"variables, functions, and arguments cannot be deleted in "
+ "ES5 strict mode");
static final DiagnosticType ILLEGAL_NAME = DiagnosticType.error(
"JSC_ILLEGAL_NAME",
"identifiers ending in '__' cannot be used in Caja");
static final DiagnosticType DUPLICATE_OBJECT_KEY = DiagnosticType.warning(
"JSC_DUPLICATE_OBJECT_KEY",
"object literals cannot contain duplicate keys in ES5 strict mode");
private final AbstractCompiler compiler;
private final boolean noVarCheck;
private final boolean noCajaChecks;
StrictModeCheck(AbstractCompiler compiler) {
this(compiler, false, false);
}
StrictModeCheck(
AbstractCompiler compiler, boolean noVarCheck, boolean noCajaChecks) {
this.compiler = compiler;
this.noVarCheck = noVarCheck;
this.noCajaChecks = noCajaChecks;
}
@Override public void process(Node externs, Node root) {
NodeTraversal.traverseRoots(
compiler, Lists.newArrayList(externs, root), this);
NodeTraversal.traverse(compiler, root, new NonExternChecks());
}
@Override public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isName()) {
if (!isDeclaration(n)) {
checkNameUse(t, n);
}
} else if (n.isAssign()) {
checkAssignment(t, n);
} else if (n.isDelProp()) {
checkDelete(t, n);
} else if (n.isObjectLit()) {
checkObjectLiteral(t, n);
} else if (n.isLabel()) {
checkLabel(t, n);
}
}
/**
* Determines if the given name is a declaration, which can be a declaration
* of a variable, function, or argument.
*/
private static boolean isDeclaration(Node n) {
switch (n.getParent().getType()) {
case Token.VAR:
case Token.FUNCTION:
case Token.CATCH:
return true;
case Token.PARAM_LIST:
return n.getParent().getParent().isFunction();
default:
return
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> goog.getMsg() node we add a pair node:sourcename and later
* when we visit its parent we remove this pair. All nodes that are left at
* the end of traversing are orphaned nodes. It means have no corresponding
* var or property node.
*/
private final Map<Node, String> googMsgNodes = Maps.newHashMap();
private final CheckLevel checkLevel;
/**
* Creates JS message visitor.
*
* @param compiler the compiler instance
* @param needToCheckDuplications whether to check duplicated messages in
* traversed
* @param style style that should be used during parsing
* @param idGenerator generator that used for creating unique ID for the
* message
*/
JsMessageVisitor(AbstractCompiler compiler,
boolean needToCheckDuplications,
JsMessage.Style style, JsMessage.IdGenerator idGenerator) {
this.compiler = compiler;
this.needToCheckDuplications = needToCheckDuplications;
this.style = style;
this.idGenerator = idGenerator;
checkLevel = (style == JsMessage.Style.CLOSURE)
? CheckLevel.ERROR : CheckLevel.WARNING;
// TODO(anatol): add flag that decides whether to process UNNAMED messages.
// Some projects would not want such functionality (unnamed) as they don't
// use SOY templates.
}
@Override
public void process(Node externs, Node root) {
NodeTraversal.traverse(compiler, root, this);
for (Map.Entry<Node, String> msgNode : googMsgNodes.entrySet()) {
compiler.report(JSError.make(msgNode.getValue(), msgNode.getKey(),
checkLevel, MESSAGE_NODE_IS_ORPHANED));
}
}
@Override
public void visit(NodeTraversal traversal, Node node, Node parent) {
String messageKey;
boolean isVar;
Node msgNode, msgNodeParent;
switch (node.getType()) {
case Token.NAME:
// var MSG_HELLO = 'Message'
if ((parent != null) && (parent.isVar())) {
messageKey = node.getString();
isVar = true;
} else {
return;
}
msgNode = node.getFirstChild();
msgNodeParent = node;
break;
case Token.ASSIGN:
// somenamespace.someclass.MSG_HELLO = 'Message'
isVar = false;
Node getProp = node.getFirstChild();
if
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> (!getProp.isGetProp()) {
return;
}
Node propNode = getProp.getLastChild();
messageKey = propNode.getString();
msgNode = node.getLastChild();
msgNodeParent = node;
break;
case Token.CALL:
// goog.getMsg()
if (MSG_FUNCTION_NAME.equals(node.getFirstChild().getQualifiedName())) {
googMsgNodes.put(node, traversal.getSourceName());
}
return;
default:
return;
}
// Is this a message name?
boolean isNewStyleMessage =
msgNode != null && msgNode.isCall();
if (!isMessageName(messageKey, isNewStyleMessage)) {
return;
}
if (msgNode == null) {
compiler.report(
traversal.makeError(node, MESSAGE_HAS_NO_VALUE, messageKey));
return;
}
// Just report a warning if a qualified messageKey that looks like a message
// (e.g. "a.b.MSG_X") doesn't use goog.getMsg().
if (isNewStyleMessage) {
googMsgNodes.remove(msgNode);
} else if (style != JsMessage.Style.LEGACY) {
compiler.report(traversal.makeError(node, checkLevel,
MESSAGE_NOT_INITIALIZED_USING_NEW_SYNTAX));
}
boolean isUnnamedMsg = isUnnamedMessageName(messageKey);
Builder builder = new Builder(
isUnnamedMsg ? null : messageKey);
builder.setSourceName(traversal.getSourceName());
try {
if (isVar) {
extractMessageFromVariable(builder, node, parent, parent.getParent());
} else {
extractMessageFromProperty(builder, node.getFirstChild(), node);
}
} catch (MalformedException ex) {
compiler.report(traversal.makeError(ex.getNode(),
MESSAGE_TREE_MALFORMED, ex.getMessage()));
return;
}
JsMessage extractedMessage = builder.build(idGenerator);
// If asked to check named internal messages.
if (needToCheckDuplications
&& !isUnnamedMsg
&& !extractedMessage.isExternal()) {
checkIfMessageDuplicated(traversal.getSourceName(), messageKey, msgNode);
}
if (extractedMessage.isEmpty()) {
// value of the message is an empty string. Translators do not like it.
compiler.report(traversal.makeError(node, MESSAGE_HAS_NO_TEXT,
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>formedException {
// Determine the message's value
Node valueNode = nameNode.getFirstChild();
switch (valueNode.getType()) {
case Token.STRING:
case Token.ADD:
maybeInitMetaDataFromJsDocOrHelpVar(builder, parentNode,
grandParentNode);
builder.appendStringPart(extractStringFromStringExprNode(valueNode));
break;
case Token.FUNCTION:
maybeInitMetaDataFromJsDocOrHelpVar(builder, parentNode,
grandParentNode);
extractFromFunctionNode(builder, valueNode);
break;
case Token.CALL:
maybeInitMetaDataFromJsDoc(builder, parentNode);
extractFromCallNode(builder, valueNode);
break;
default:
throw new MalformedException("Cannot parse value of message "
+ builder.getKey(), valueNode);
}
}
/**
* Creates a {@link JsMessage} for a JS message defined using an assignment to
* a qualified name (e.g <code>a.b.MSG_X = goog.getMsg(...);</code>).
*
* @param builder the message builder
* @param getPropNode a GETPROP node in a JS message assignment
* @param assignNode an ASSIGN node, parent of {@code getPropNode}.
* @throws MalformedException if {@code getPropNode} does not
* correspond to a valid JS message node
*/
private void extractMessageFromProperty(
Builder builder, Node getPropNode, Node assignNode)
throws MalformedException {
Node callNode = getPropNode.getNext();
maybeInitMetaDataFromJsDoc(builder, assignNode);
extractFromCallNode(builder, callNode);
}
/**
* Initializes the meta data in a JsMessage by examining the nodes just before
* and after a message VAR node.
*
* @param builder the message builder whose meta data will be initialized
* @param varNode the message VAR node
* @param parentOfVarNode {@code varNode}'s parent node
*/
private void maybeInitMetaDataFromJsDocOrHelpVar(
Builder builder, Node varNode, @Nullable Node parentOfVarNode)
throws MalformedException {
// First check description in @desc
if (maybeInitMetaDataFromJsDoc(builder, varNode)) {
return;
}
// Check the preceding node for meta data
if ((parentOfVarNode != null) &&
maybeInitMetaDataFromHelpVar(builder,
parentOfVarNode.getChildBefore(varNode))) {
return;
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> String representation of the node
* @throws MalformedException if the parsed message is invalid
*/
private static String extractStringFromStringExprNode(Node node)
throws MalformedException {
switch (node.getType()) {
case Token.STRING:
return node.getString();
case Token.ADD:
StringBuilder sb = new StringBuilder();
for (Node child : node.children()) {
sb.append(extractStringFromStringExprNode(child));
}
return sb.toString();
default:
throw new MalformedException("STRING or ADD node expected; found: " +
getReadableTokenName(node), node);
}
}
/**
* Initializes a message builder from a FUNCTION node.
* <p>
* <pre>
* The tree should look something like:
*
* function
* |-- name
* |-- lp
* | |-- name <arg1>
* | -- name <arg2>
* -- block
* |
* --return
* |
* --add
* |-- string foo
* -- name <arg1>
* </pre>
*
* @param builder the message builder
* @param node the function node that contains a message
* @throws MalformedException if the parsed message is invalid
*/
private void extractFromFunctionNode(Builder builder, Node node)
throws MalformedException {
Set<String> phNames = Sets.newHashSet();
for (Node fnChild : node.children()) {
switch (fnChild.getType()) {
case Token.NAME:
// This is okay. The function has a name, but it is empty.
break;
case Token.PARAM_LIST:
// Parse the placeholder names from the function argument list.
for (Node argumentNode : fnChild.children()) {
if (argumentNode.isName()) {
String phName = argumentNode.getString();
if (phNames.contains(phName)) {
throw new MalformedException("Duplicate placeholder name: "
+ phName, argumentNode);
} else {
phNames.add(phName);
}
}
}
break;
case Token.BLOCK:
// Build the message's value by examining the return statement
Node returnNode = fnChild.getFirstChild();
if (!returnNode.isReturn()) {
throw new MalformedException("RETURN node expected; found: "
+ getReadableTokenName(returnNode), returnNode);
}
for (Node child : returnNode.
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>children()) {
extractFromReturnDescendant(builder, child);
}
// Check that all placeholders from the message text have appropriate
// object literal keys
for (String phName : builder.getPlaceholders()) {
if(!phNames.contains(phName)) {
throw new MalformedException(
"Unrecognized message placeholder referenced: " + phName,
returnNode);
}
}
break;
default:
throw new MalformedException(
"NAME, LP, or BLOCK node expected; found: "
+ getReadableTokenName(node), fnChild);
}
}
}
/**
* Appends value parts to the message builder by traversing the descendants
* of the given RETURN node.
*
* @param builder the message builder
* @param node the node from where we extract a message
* @throws MalformedException if the parsed message is invalid
*/
private void extractFromReturnDescendant(Builder builder, Node node)
throws MalformedException {
switch (node.getType()) {
case Token.STRING:
builder.appendStringPart(node.getString());
break;
case Token.NAME:
builder.appendPlaceholderReference(node.getString());
break;
case Token.ADD:
for (Node child : node.children()) {
extractFromReturnDescendant(builder, child);
}
break;
default:
throw new MalformedException(
"STRING, NAME, or ADD node expected; found: "
+ getReadableTokenName(node), node);
}
}
/**
* Initializes a message builder from a CALL node.
* <p>
* The tree should look something like:
*
* <pre>
* call
* |-- getprop
* | |-- name 'goog'
* | +-- string 'getMsg'
* |
* |-- string 'Hi {$userName}! Welcome to {$product}.'
* +-- objlit
* |-- string 'userName'
* |-- name 'someUserName'
* |-- string 'product'
* +-- call
* +-- name 'getProductName'
* </pre>
*
* @param builder the message builder
* @param node the call node from where we extract the message
* @throws MalformedException if the parsed message is invalid
*/
private void extractFromCallNode(Builder builder,
Node node) throws MalformedException {
// Check the function being called
if (!node.isCall()) {
throw new
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>.CLOSURE || isNewStyleMessage ||
!identifier.endsWith(DESC_SUFFIX));
}
/**
* Returns whether the given message name is in the unnamed namespace.
*/
private static boolean isUnnamedMessageName(String identifier) {
return MSG_UNNAMED_PATTERN.matcher(identifier).matches();
}
/**
* Returns whether a string is nonempty, begins with a lowercase letter, and
* contains only digits and underscores after the first underscore.
*/
static boolean isLowerCamelCaseWithNumericSuffixes(String input) {
return CAMELCASE_PATTERN.matcher(input).matches();
}
/**
* Returns human-readable name of the given node's type.
*/
private static String getReadableTokenName(Node node) {
return Token.name(node.getType());
}
/**
* Converts the given string from upper-underscore case to lower-camel case,
* preserving numeric suffixes. For example: "NAME" -> "name" "A4_LETTER" ->
* "a4Letter" "START_SPAN_1_23" -> "startSpan_1_23".
*/
static String toLowerCamelCaseWithNumericSuffixes(String input) {
// Determine where the numeric suffixes begin
int suffixStart = input.length();
while (suffixStart > 0) {
char ch = '\0';
int numberStart = suffixStart;
while (numberStart > 0) {
ch = input.charAt(numberStart - 1);
if (Character.isDigit(ch)) {
numberStart--;
} else {
break;
}
}
if ((numberStart > 0) && (numberStart < suffixStart) && (ch == '_')) {
suffixStart = numberStart - 1;
} else {
break;
}
}
if (suffixStart == input.length()) {
return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, input);
} else {
return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL,
input.substring(0, suffixStart)) +
input.substring(suffixStart);
}
}
static class MalformedException extends Exception {
private static final long serialVersionUID = 1L;
private final Node node;
MalformedException(String message, Node node) {
super(message);
this.node = node;
}
Node getNode() {
return node;
}
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>/*
* Copyright 2008 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
/**
* Check for usage of 'with'.
*
*/
class ControlStructureCheck implements HotSwapCompilerPass {
private final AbstractCompiler compiler;
static final DiagnosticType USE_OF_WITH = DiagnosticType.warning(
"JSC_USE_OF_WITH",
"The use of the 'with' structure should be avoided.");
ControlStructureCheck(AbstractCompiler compiler) {
this.compiler = compiler;
}
@Override
public void process(Node externs, Node root) {
check(root);
}
@Override
public void hotSwapScript(Node scriptRoot, Node originalRoot) {
check(scriptRoot);
}
/**
* Reports errors for any invalid use of control structures.
*
* @param node Current node to check.
*/
private void check(Node node) {
switch (node.getType()) {
case Token.WITH:
JSDocInfo info = node.getJSDocInfo();
boolean allowWith =
info != null && info.getSuppressions().contains("with");
if (!allowWith) {
report(node, USE_OF_WITH);
}
break;
}
for (Node bChild = node.getFirstChild(); bChild != null;) {
Node next = bChild.getNext();
check(bChild);
bChild = next;
}
}
private void report(Node n, DiagnosticType error) {
compiler.report(JSError.make(n.getSourceFileName(), n, error));
}
}
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>(lValue.getJSType());
}
} else if (NodeUtil.isFunctionDeclaration(n) ||
parent.isName()) {
return normalizeClassType(n.getJSType());
}
return null;
}
/**
* Normalize the type of a constructor, its instance, and its prototype
* all down to the same type (the instance type).
*/
private JSType normalizeClassType(JSType type) {
if (type == null || type.isUnknownType()) {
return type;
} else if (type.isNominalConstructor()) {
return (type.toMaybeFunctionType()).getInstanceType();
} else if (type.isFunctionPrototypeType()) {
FunctionType owner = ((ObjectType) type).getOwnerFunction();
if (owner.isConstructor()) {
return owner.getInstanceType();
}
}
return type;
}
@Override
public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
return true;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.NAME:
checkNameDeprecation(t, n, parent);
checkNameVisibility(t, n, parent);
break;
case Token.GETPROP:
checkPropertyDeprecation(t, n, parent);
checkPropertyVisibility(t, n, parent);
checkConstantProperty(t, n);
break;
case Token.NEW:
checkConstructorDeprecation(t, n, parent);
break;
}
}
/**
* Checks the given NEW node to ensure that access restrictions are obeyed.
*/
private void checkConstructorDeprecation(NodeTraversal t, Node n,
Node parent) {
JSType type = n.getJSType();
if (type != null) {
String deprecationInfo = getTypeDeprecationInfo(type);
if (deprecationInfo != null &&
shouldEmitDeprecationWarning(t, n, parent)) {
if (!deprecationInfo.isEmpty()) {
compiler.report(
t.makeError(n, DEPRECATED_CLASS_REASON,
type.toString(), deprecationInfo));
} else {
compiler.report(
t.makeError(n, DEPRECATED_CLASS, type.toString()));
}
}
}
}
/**
* Checks the given NAME node to ensure that access restrictions are obeyed.
*/
private void checkNameDeprecation(NodeTraversal t, Node n, Node parent) {
// Don't bother checking definitions or
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>/*
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Rhino code, released
* May 6, 1999.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1997-1999
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bob Jervis
* Google Inc.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU General Public License Version 2 or later (the "GPL"), in which
* case the provisions of the GPL are applicable instead of those above. If
* you wish to allow use of your version of this file only under the terms of
* the GPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replacing
* them with the notice and other provisions required by the GPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the GPL.
*
* ***** END LICENSE BLOCK ***** */
package com.google.javascript.rhino.jstype;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
/**
* A builder for the Rhino Node representing Function parameters.
* @author nicksantos@google.com (Nick Santos)
*/
public class FunctionParamBuilder {
private final JSTypeRegistry registry;
private final Node root = new Node(Token.PARAM_LIST);
public FunctionParamBuilder(JSTypeRegistry registry) {
this.registry = registry;
}
/**
* Add parameters of the given type to the end of the param list.
* @return False
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>.newString(Token.NAME, "");
paramNode.setJSType(type);
root.addChildToBack(paramNode);
return paramNode;
}
public Node build() {
return root;
}
private boolean hasOptionalOrVarArgs() {
Node lastChild = root.getLastChild();
return lastChild != null &&
(lastChild.isOptionalArg() || lastChild.isVarArgs());
}
public boolean hasVarArgs() {
Node lastChild = root.getLastChild();
return lastChild != null && lastChild.isVarArgs();
}
}
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>nowingConditionOutcome(
condition, blindScope, outcome) : blindScope;
}
/**
* Returns the type of a node in the given scope if the node corresponds to a
* name whose type is capable of being refined.
* @return The current type of the node if it can be refined, null otherwise.
*/
protected JSType getTypeIfRefinable(Node node, FlowScope scope) {
switch (node.getType()) {
case Token.NAME:
StaticSlot<JSType> nameVar = scope.getSlot(node.getString());
if (nameVar != null) {
JSType nameVarType = nameVar.getType();
if (nameVarType == null) {
nameVarType = node.getJSType();
}
return nameVarType;
}
return null;
case Token.GETPROP:
String qualifiedName = node.getQualifiedName();
if (qualifiedName == null) {
return null;
}
StaticSlot<JSType> propVar = scope.getSlot(qualifiedName);
JSType propVarType = null;
if (propVar != null) {
propVarType = propVar.getType();
}
if (propVarType == null) {
propVarType = node.getJSType();
}
if (propVarType == null) {
propVarType = getNativeType(UNKNOWN_TYPE);
}
return propVarType;
}
return null;
}
/**
* Declares a refined type in {@code scope} for the name represented by
* {@code node}. It must be possible to refine the type of the given node in
* the given scope, as determined by {@link #getTypeIfRefinable}.
*/
protected void declareNameInScope(FlowScope scope, Node node, JSType type) {
switch (node.getType()) {
case Token.NAME:
scope.inferSlotType(node.getString(), type);
break;
case Token.GETPROP:
String qualifiedName = node.getQualifiedName();
Preconditions.checkNotNull(qualifiedName);
JSType origType = node.getJSType();
origType = origType == null ? getNativeType(UNKNOWN_TYPE) : origType;
scope.inferQualifiedSlot(node, qualifiedName, origType, type);
break;
default:
throw new IllegalArgumentException("Node cannot be refined. \n" +
node.toStringTree());
}
}
/**
* @see #getRestrictedWithoutUndefined(JSType
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>/*
* Copyright 2010 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import com.google.common.collect.Maps;
import com.google.javascript.rhino.JSDocInfo;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.Token;
import java.util.Map;
/**
* Filters warnings based on in-code {@code @suppress} annotations.
* @author nicksantos@google.com (Nick Santos)
*/
class SuppressDocWarningsGuard extends WarningsGuard {
private static final long serialVersionUID = 1L;
/** Warnings guards for each suppressible warnings group, indexed by name. */
private final Map<String, DiagnosticGroupWarningsGuard> suppressors =
Maps.newHashMap();
/**
* The suppressible groups, indexed by name.
*/
SuppressDocWarningsGuard(Map<String, DiagnosticGroup> suppressibleGroups) {
for (Map.Entry<String, DiagnosticGroup> entry :
suppressibleGroups.entrySet()) {
suppressors.put(
entry.getKey(),
new DiagnosticGroupWarningsGuard(
entry.getValue(),
CheckLevel.OFF));
}
}
@Override
public CheckLevel level(JSError error) {
Node node = error.node;
if (node != null) {
for (Node current = node;
current != null;
current = current.getParent()) {
int type = current.getType();
JSDocInfo info = null;
// We only care about function annotations at the FUNCTION and SCRIPT
// level. Otherwise, the @suppress annotation has an implicit
// dependency on the exact structure of our AST, and that seems like
// a bad idea.
if (type == Token.FUNCTION) {
info = NodeUtil.getFunctionJSDocInfo(current);
} else
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> if (type == Token.SCRIPT) {
info = current.getJSDocInfo();
} else if (type == Token.ASSIGN) {
Node rhs = current.getLastChild();
if (rhs.isFunction()) {
info = NodeUtil.getFunctionJSDocInfo(rhs);
}
}
if (info != null) {
for (String suppressor : info.getSuppressions()) {
WarningsGuard guard = suppressors.get(suppressor);
// Some @suppress tags are for other tools, and
// may not have a warnings guard.
if (guard != null) {
CheckLevel newLevel = guard.level(error);
if (newLevel != null) {
return newLevel;
}
}
}
}
}
}
return null;
}
@Override
public int getPriority() {
// Happens after path-based filtering, but before other times
// of filtering.
return WarningsGuard.Priority.SUPPRESS_DOC.value;
}
}
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> clauseNode);
abstract T processConditionalExpression(ConditionalExpression exprNode);
abstract T processContinueStatement(ContinueStatement statementNode);
abstract T processDoLoop(DoLoop loopNode);
abstract T processElementGet(ElementGet getNode);
abstract T processEmptyExpression(EmptyExpression exprNode);
abstract T processEmptyStatement(EmptyStatement exprNode);
abstract T processExpressionStatement(ExpressionStatement statementNode);
abstract T processForInLoop(ForInLoop loopNode);
abstract T processForLoop(ForLoop loopNode);
abstract T processFunctionCall(FunctionCall callNode);
abstract T processFunctionNode(FunctionNode functionNode);
abstract T processIfStatement(IfStatement statementNode);
abstract T processInfixExpression(InfixExpression exprNode);
abstract T processKeywordLiteral(KeywordLiteral literalNode);
abstract T processLabel(Label labelNode);
abstract T processLabeledStatement(LabeledStatement statementNode);
abstract T processName(Name nameNode);
abstract T processNewExpression(NewExpression exprNode);
abstract T processNumberLiteral(NumberLiteral literalNode);
abstract T processObjectLiteral(ObjectLiteral literalNode);
abstract T processObjectProperty(ObjectProperty propertyNode);
abstract T processParenthesizedExpression(ParenthesizedExpression exprNode);
abstract T processPropertyGet(PropertyGet getNode);
abstract T processRegExpLiteral(RegExpLiteral literalNode);
abstract T processReturnStatement(ReturnStatement statementNode);
abstract T processScope(Scope scopeNode);
abstract T processStringLiteral(StringLiteral literalNode);
abstract T processSwitchCase(SwitchCase caseNode);
abstract T processSwitchStatement(SwitchStatement statementNode);
abstract T processThrowStatement(ThrowStatement statementNode);
abstract T processTryStatement(TryStatement statementNode);
abstract T processUnaryExpression(UnaryExpression exprNode);
abstract T processVariableDeclaration(VariableDeclaration declarationNode);
abstract T processVariableInitializer(VariableInitializer initializerNode);
abstract T processWhileLoop(WhileLoop loopNode);
abstract T processWithStatement(WithStatement statementNode);
abstract T processIllegalToken(AstNode node);
public T process(AstNode node) {
switch (node.getType()) {
case Token.ADD:
case Token.AND:
case Token.BITAND:
case Token.BITOR:
case Token.BITXOR:
case Token.COMMA:
case Token.DIV:
case Token.EQ:
case Token.GE:
case Token.GT:
case Token.IN:
case Token.INSTANCEOF:
case Token.LE:
case Token.LSH:
case Token.LT:
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>
case Token.MOD:
case Token.MUL:
case Token.NE:
case Token.OR:
case Token.RSH:
case Token.SHEQ:
case Token.SHNE:
case Token.SUB:
case Token.URSH:
return processInfixExpression((InfixExpression) node);
case Token.ARRAYLIT:
return processArrayLiteral((ArrayLiteral) node);
case Token.ASSIGN:
case Token.ASSIGN_ADD:
case Token.ASSIGN_BITAND:
case Token.ASSIGN_BITOR:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_DIV:
case Token.ASSIGN_LSH:
case Token.ASSIGN_MOD:
case Token.ASSIGN_MUL:
case Token.ASSIGN_RSH:
case Token.ASSIGN_SUB:
case Token.ASSIGN_URSH:
return processAssignment((Assignment) node);
case Token.BITNOT:
case Token.DEC:
case Token.DELPROP:
case Token.INC:
case Token.NEG:
case Token.NOT:
case Token.POS:
case Token.TYPEOF:
case Token.VOID:
return processUnaryExpression((UnaryExpression) node);
case Token.BLOCK:
if (node instanceof Block) {
return processBlock((Block) node);
} else if (node instanceof Scope) {
return processScope((Scope) node);
} else {
throw new IllegalStateException("Unexpected node type. class: " +
node.getClass() +
" type: " +
Token.typeToName(node.getType()));
}
case Token.BREAK:
return processBreakStatement((BreakStatement) node);
case Token.CALL:
return processFunctionCall((FunctionCall) node);
case Token.CASE:
case Token.DEFAULT:
return processSwitchCase((SwitchCase) node);
case Token.CATCH:
return processCatchClause((CatchClause) node);
case Token.COLON:
return processObjectProperty((ObjectProperty) node);
case Token.CONTINUE:
return processContinueStatement((ContinueStatement) node);
case Token.DO:
return processDoLoop((DoLoop) node);
case Token.EMPTY:
return (node instanceof EmptyExpression) ?
processEmptyExpression((EmptyExpression) node) :
processEmptyStatement((EmptyStatement) node);
case Token.EXPR_RESULT:
case Token.EXPR_VOID:
if (node instanceof ExpressionStatement) {
return processExpressionStatement((ExpressionStatement) node);
} else if
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> (node instanceof LabeledStatement) {
return processLabeledStatement((LabeledStatement) node);
} else {
throw new IllegalStateException("Unexpected node type. class: " +
node.getClass() +
" type: " +
Token.typeToName(node.getType()));
}
case Token.DEBUGGER:
case Token.FALSE:
case Token.NULL:
case Token.THIS:
case Token.TRUE:
return processKeywordLiteral((KeywordLiteral) node);
case Token.FOR:
if (node instanceof ForInLoop) {
return processForInLoop((ForInLoop) node);
} else if (node instanceof ForLoop) {
return processForLoop((ForLoop) node);
} else {
throw new IllegalStateException("Unexpected node type. class: " +
node.getClass() +
" type: " +
Token.typeToName(node.getType()));
}
case Token.FUNCTION:
return processFunctionNode((FunctionNode) node);
case Token.GETELEM:
return processElementGet((ElementGet) node);
case Token.GETPROP:
return processPropertyGet((PropertyGet) node);
case Token.HOOK:
return processConditionalExpression((ConditionalExpression) node);
case Token.IF:
return processIfStatement((IfStatement) node);
case Token.LABEL:
return processLabel((Label) node);
case Token.LP:
return processParenthesizedExpression((ParenthesizedExpression) node);
case Token.NAME:
return processName((Name) node);
case Token.NEW:
return processNewExpression((NewExpression) node);
case Token.NUMBER:
return processNumberLiteral((NumberLiteral) node);
case Token.OBJECTLIT:
return processObjectLiteral((ObjectLiteral) node);
case Token.REGEXP:
return processRegExpLiteral((RegExpLiteral) node);
case Token.RETURN:
return processReturnStatement((ReturnStatement) node);
case Token.SCRIPT:
return processAstRoot((AstRoot) node);
case Token.STRING:
return processStringLiteral((StringLiteral) node);
case Token.SWITCH:
return processSwitchStatement((SwitchStatement) node);
case Token.THROW:
return processThrowStatement((ThrowStatement) node);
case Token.TRY:
return processTryStatement((TryStatement) node);
case Token.CONST:
case Token.VAR:
if (node instanceof VariableDeclaration) {
return processVariableDeclaration((VariableDeclaration) node);
} else if (node instanceof VariableInitializer) {
return processVariable
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>Initializer((VariableInitializer) node);
} else {
throw new IllegalStateException("Unexpected node type. class: " +
node.getClass() +
" type: " +
Token.typeToName(node.getType()));
}
case Token.WHILE:
return processWhileLoop((WhileLoop) node);
case Token.WITH:
return processWithStatement((WithStatement) node);
}
return processIllegalToken(node);
}
}
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> Set<String> CONSTRUCTORS_WITHOUT_SIDE_EFFECTS =
new HashSet<String>(Arrays.asList(
"Array",
"Date",
"Error",
"Object",
"RegExp",
"XMLHttpRequest"));
// Utility class; do not instantiate.
private NodeUtil() {}
/**
* Gets the boolean value of a node that represents a expression. This method
* effectively emulates the <code>Boolean()</code> JavaScript cast function.
* Note: unlike getBooleanValue this function does not return UNKNOWN
* for expressions with side-effects.
*/
static TernaryValue getImpureBooleanValue(Node n) {
switch (n.getType()) {
case Token.ASSIGN:
case Token.COMMA:
// For ASSIGN and COMMA the value is the value of the RHS.
return getImpureBooleanValue(n.getLastChild());
case Token.NOT:
TernaryValue value = getImpureBooleanValue(n.getLastChild());
return value.not();
case Token.AND: {
TernaryValue lhs = getImpureBooleanValue(n.getFirstChild());
TernaryValue rhs = getImpureBooleanValue(n.getLastChild());
return lhs.and(rhs);
}
case Token.OR: {
TernaryValue lhs = getImpureBooleanValue(n.getFirstChild());
TernaryValue rhs = getImpureBooleanValue(n.getLastChild());
return lhs.or(rhs);
}
case Token.HOOK: {
TernaryValue trueValue = getImpureBooleanValue(
n.getFirstChild().getNext());
TernaryValue falseValue = getImpureBooleanValue(n.getLastChild());
if (trueValue.equals(falseValue)) {
return trueValue;
} else {
return TernaryValue.UNKNOWN;
}
}
case Token.ARRAYLIT:
case Token.OBJECTLIT:
// ignoring side-effects
return TernaryValue.TRUE;
case Token.VOID:
return TernaryValue.FALSE;
default:
return getPureBooleanValue(n);
}
}
/**
* Gets the boolean value of a node that represents a literal. This method
* effectively emulates the <code>Boolean()</code> JavaScript cast function
* except it return UNKNOWN for known values with side-effects, use
* getExpressionBooleanValue if you don't care about side-effects.
*/
static TernaryValue getPureBooleanValue(Node n) {
switch (n.getType()) {
case Token
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>.STRING:
return TernaryValue.forBoolean(n.getString().length() > 0);
case Token.NUMBER:
return TernaryValue.forBoolean(n.getDouble() != 0);
case Token.NOT:
return getPureBooleanValue(n.getLastChild()).not();
case Token.NULL:
case Token.FALSE:
return TernaryValue.FALSE;
case Token.VOID:
if (!mayHaveSideEffects(n.getFirstChild())) {
return TernaryValue.FALSE;
}
break;
case Token.NAME:
String name = n.getString();
if ("undefined".equals(name)
|| "NaN".equals(name)) {
// We assume here that programs don't change the value of the keyword
// undefined to something other than the value undefined.
return TernaryValue.FALSE;
} else if ("Infinity".equals(name)) {
return TernaryValue.TRUE;
}
break;
case Token.TRUE:
case Token.REGEXP:
return TernaryValue.TRUE;
case Token.ARRAYLIT:
case Token.OBJECTLIT:
if (!mayHaveSideEffects(n)) {
return TernaryValue.TRUE;
}
break;
}
return TernaryValue.UNKNOWN;
}
/**
* Gets the value of a node as a String, or null if it cannot be converted.
* When it returns a non-null String, this method effectively emulates the
* <code>String()</code> JavaScript cast function.
*/
static String getStringValue(Node n) {
// TODO(user): regex literals as well.
switch (n.getType()) {
case Token.STRING:
case Token.STRING_KEY:
return n.getString();
case Token.NAME:
String name = n.getString();
if ("undefined".equals(name)
|| "Infinity".equals(name)
|| "NaN".equals(name)) {
return name;
}
break;
case Token.NUMBER:
return getStringValue(n.getDouble());
case Token.FALSE:
return "false";
case Token.TRUE:
return "true";
case Token.NULL:
return "null";
case Token.VOID:
return "undefined";
case Token.NOT:
TernaryValue child = getPureBooleanValue(n.getFirstChild());
if (child != TernaryValue.UNKNOWN) {
return child.toBoolean(true) ? "false" : "true"; // reversed.
}
break;
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>
case Token.ARRAYLIT:
return arrayToString(n);
case Token.OBJECTLIT:
return "[object Object]";
}
return null;
}
static String getStringValue(double value) {
long longValue = (long) value;
// Return "1" instead of "1.0"
if (longValue == value) {
return Long.toString(longValue);
} else {
return Double.toString(value);
}
}
/**
* When converting arrays to string using Array.prototype.toString or
* Array.prototype.join, the rules for conversion to String are different
* than converting each element individually. Specifically, "null" and
* "undefined" are converted to an empty string.
* @param n A node that is a member of an Array.
* @return The string representation.
*/
static String getArrayElementStringValue(Node n) {
return (NodeUtil.isNullOrUndefined(n) || n.isEmpty())
? "" : getStringValue(n);
}
static String arrayToString(Node literal) {
Node first = literal.getFirstChild();
StringBuilder result = new StringBuilder();
int nextSlot = 0;
int nextSkipSlot = 0;
for (Node n = first; n != null; n = n.getNext()) {
String childValue = getArrayElementStringValue(n);
if (childValue == null) {
return null;
}
if (n != first) {
result.append(',');
}
result.append(childValue);
nextSlot++;
}
return result.toString();
}
/**
* Gets the value of a node as a Number, or null if it cannot be converted.
* When it returns a non-null Double, this method effectively emulates the
* <code>Number()</code> JavaScript cast function.
*/
static Double getNumberValue(Node n) {
switch (n.getType()) {
case Token.TRUE:
return 1.0;
case Token.FALSE:
case Token.NULL:
return 0.0;
case Token.NUMBER:
return n.getDouble();
case Token.VOID:
if (mayHaveSideEffects(n.getFirstChild())) {
return null;
} else {
return Double.NaN;
}
case Token.NAME:
// Check for known constants
String name = n.getString();
if (name.equals("undefined")) {
return Double.NaN;
}
if (name.equals("NaN")) {
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> return Double.NaN;
}
if (name.equals("Infinity")) {
return Double.POSITIVE_INFINITY;
}
return null;
case Token.NEG:
if (n.getChildCount() == 1 && n.getFirstChild().isName()
&& n.getFirstChild().getString().equals("Infinity")) {
return Double.NEGATIVE_INFINITY;
}
return null;
case Token.NOT:
TernaryValue child = getPureBooleanValue(n.getFirstChild());
if (child != TernaryValue.UNKNOWN) {
return child.toBoolean(true) ? 0.0 : 1.0; // reversed.
}
break;
case Token.STRING:
return getStringNumberValue(n.getString());
case Token.ARRAYLIT:
case Token.OBJECTLIT:
String value = getStringValue(n);
return value != null ? getStringNumberValue(value) : null;
}
return null;
}
static Double getStringNumberValue(String rawJsString) {
if (rawJsString.contains("\u000b")) {
// vertical tab is not always whitespace
return null;
}
String s = trimJsWhiteSpace(rawJsString);
// return ScriptRuntime.toNumber(s);
if (s.length() == 0) {
return 0.0;
}
if (s.length() > 2
&& s.charAt(0) == '0'
&& (s.charAt(1) == 'x' || s.charAt(1) == 'X')) {
// Attempt to convert hex numbers.
try {
return Double.valueOf(Integer.parseInt(s.substring(2), 16));
} catch (NumberFormatException e) {
return Double.NaN;
}
}
if (s.length() > 3
&& (s.charAt(0) == '-' || s.charAt(0) == '+')
&& s.charAt(1) == '0'
&& (s.charAt(2) == 'x' || s.charAt(2) == 'X')) {
// hex numbers with explicit signs vary between browsers.
return null;
}
// Firefox and IE treat the "Infinity" differently. Firefox is case
// insensitive, but IE treats "infinity" as NaN. So leave it alone.
if (s.equals("infinity")
|| s.equals("-infinity")
|| s.equals("+infinity")) {
return null;
}
try {
return Double.
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> {
case Token.NAME:
// var name = function() ...
// var name2 = function name1() ...
return parent.getString();
case Token.ASSIGN:
// qualified.name = function() ...
// qualified.name2 = function name1() ...
return parent.getFirstChild().getQualifiedName();
default:
// function name() ...
return name != null && name.length() != 0 ? name : null;
}
}
/**
* Gets the function's name. This method recognizes the forms:
* <ul>
* <li>{@code {'name': function() ...}}</li>
* <li>{@code {name: function() ...}}</li>
* <li>{@code function name() ...}</li>
* <li>{@code var name = function() ...}</li>
* <li>{@code qualified.name = function() ...}</li>
* <li>{@code var name2 = function name1() ...}</li>
* <li>{@code qualified.name2 = function name1() ...}</li>
* </ul>
*
* @param n a node whose type is {@link Token#FUNCTION}
* @return the function's name, or {@code null} if it has no name
*/
public static String getNearestFunctionName(Node n) {
String name = getFunctionName(n);
if (name != null) {
return name;
}
// Check for the form { 'x' : function() { } }
Node parent = n.getParent();
switch (parent.getType()) {
case Token.SETTER_DEF:
case Token.GETTER_DEF:
case Token.STRING_KEY:
// Return the name of the literal's key.
return parent.getString();
case Token.NUMBER:
return getStringValue(parent);
}
return null;
}
/**
* Returns true if this is an immutable value.
*/
static boolean isImmutableValue(Node n) {
switch (n.getType()) {
case Token.STRING:
case Token.NUMBER:
case Token.NULL:
case Token.TRUE:
case Token.FALSE:
return true;
case Token.NOT:
return isImmutableValue(n.getFirstChild());
case Token.VOID:
case Token.NEG:
return isImmutableValue(n.getFirstChild());
case Token.NAME:
String name = n.getString();
// We assume here
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> that programs don't change the value of the keyword
// undefined to something other than the value undefined.
return "undefined".equals(name)
|| "Infinity".equals(name)
|| "NaN".equals(name);
}
return false;
}
/**
* Returns true if the operator on this node is symmetric
*/
public static boolean isSymmetricOperation(Node n) {
switch (n.getType()) {
case Token.EQ: // equal
case Token.NE: // not equal
case Token.SHEQ: // exactly equal
case Token.SHNE: // exactly not equal
case Token.MUL: // multiply, unlike add it only works on numbers
// or results NaN if any of the operators is not a number
return true;
}
return false;
}
/**
* Returns true if the operator on this node is relational.
* the returned set does not include the equalities.
*/
public static boolean isRelationalOperation(Node n) {
switch (n.getType()) {
case Token.GT: // equal
case Token.GE: // not equal
case Token.LT: // exactly equal
case Token.LE: // exactly not equal
return true;
}
return false;
}
/**
* Returns the inverse of an operator if it is invertible.
* ex. '>' ==> '<'
*/
public static int getInverseOperator(int type) {
switch (type) {
case Token.GT:
return Token.LT;
case Token.LT:
return Token.GT;
case Token.GE:
return Token.LE;
case Token.LE:
return Token.GE;
}
return Token.ERROR;
}
/**
* Returns true if this is a literal value. We define a literal value
* as any node that evaluates to the same thing regardless of when or
* where it is evaluated. So /xyz/ and [3, 5] are literals, but
* the name a is not.
*
* Function literals do not meet this definition, because they
* lexically capture variables. For example, if you have
* <code>
* function() { return a; }
* </code>
* If it is evaluated in a different scope, then it
* captures a different variable. Even if the function did not read
* any captured variables directly, it would still fail this definition,
* because it affects the lifecycle of variables in the enclosing scope.
*
* However, a function literal with respect to a particular
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> scope is
* a literal.
*
* @param includeFunctions If true, all function expressions will be
* treated as literals.
*/
static boolean isLiteralValue(Node n, boolean includeFunctions) {
switch (n.getType()) {
case Token.ARRAYLIT:
for (Node child = n.getFirstChild(); child != null;
child = child.getNext()) {
if ((!child.isEmpty()) && !isLiteralValue(child, includeFunctions)) {
return false;
}
}
return true;
case Token.REGEXP:
// Return true only if all children are const.
for (Node child = n.getFirstChild(); child != null;
child = child.getNext()) {
if (!isLiteralValue(child, includeFunctions)) {
return false;
}
}
return true;
case Token.OBJECTLIT:
// Return true only if all values are const.
for (Node child = n.getFirstChild(); child != null;
child = child.getNext()) {
if (!isLiteralValue(child.getFirstChild(), includeFunctions)) {
return false;
}
}
return true;
case Token.FUNCTION:
return includeFunctions && !NodeUtil.isFunctionDeclaration(n);
default:
return isImmutableValue(n);
}
}
/**
* Determines whether the given value may be assigned to a define.
*
* @param val The value being assigned.
* @param defines The list of names of existing defines.
*/
static boolean isValidDefineValue(Node val, Set<String> defines) {
switch (val.getType()) {
case Token.STRING:
case Token.NUMBER:
case Token.TRUE:
case Token.FALSE:
return true;
// Binary operators are only valid if both children are valid.
case Token.ADD:
case Token.BITAND:
case Token.BITNOT:
case Token.BITOR:
case Token.BITXOR:
case Token.DIV:
case Token.EQ:
case Token.GE:
case Token.GT:
case Token.LE:
case Token.LSH:
case Token.LT:
case Token.MOD:
case Token.MUL:
case Token.NE:
case Token.RSH:
case Token.SHEQ:
case Token.SHNE:
case Token.SUB:
case Token.URSH:
return isValidDefineValue(val.getFirstChild(), defines)
&& isValidDefineValue(val.getLastChild(), defines);
// Unary
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> operators are valid if the child is valid.
case Token.NOT:
case Token.NEG:
case Token.POS:
return isValidDefineValue(val.getFirstChild(), defines);
// Names are valid if and only if they are defines themselves.
case Token.NAME:
case Token.GETPROP:
if (val.isQualifiedName()) {
return defines.contains(val.getQualifiedName());
}
}
return false;
}
/**
* Returns whether this a BLOCK node with no children.
*
* @param block The node.
*/
static boolean isEmptyBlock(Node block) {
if (!block.isBlock()) {
return false;
}
for (Node n = block.getFirstChild(); n != null; n = n.getNext()) {
if (!n.isEmpty()) {
return false;
}
}
return true;
}
static boolean isSimpleOperator(Node n) {
return isSimpleOperatorType(n.getType());
}
/**
* A "simple" operator is one whose children are expressions,
* has no direct side-effects (unlike '+='), and has no
* conditional aspects (unlike '||').
*/
static boolean isSimpleOperatorType(int type) {
switch (type) {
case Token.ADD:
case Token.BITAND:
case Token.BITNOT:
case Token.BITOR:
case Token.BITXOR:
case Token.COMMA:
case Token.DIV:
case Token.EQ:
case Token.GE:
case Token.GETELEM:
case Token.GETPROP:
case Token.GT:
case Token.INSTANCEOF:
case Token.LE:
case Token.LSH:
case Token.LT:
case Token.MOD:
case Token.MUL:
case Token.NE:
case Token.NOT:
case Token.RSH:
case Token.SHEQ:
case Token.SHNE:
case Token.SUB:
case Token.TYPEOF:
case Token.VOID:
case Token.POS:
case Token.NEG:
case Token.URSH:
return true;
default:
return false;
}
}
/**
* Creates an EXPR_RESULT.
*
* @param child The expression itself.
* @return Newly created EXPR node with the child as subexpression.
*/
public static Node newExpr(Node child) {
return IR.exprResult(child).srcref(child);
}
/**
* Returns
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> true if the node may create new mutable state, or change existing
* state.
*
* @see <a href="http://www.xkcd.org/326/">XKCD Cartoon</a>
*/
static boolean mayEffectMutableState(Node n) {
return mayEffectMutableState(n, null);
}
static boolean mayEffectMutableState(Node n, AbstractCompiler compiler) {
return checkForStateChangeHelper(n, true, compiler);
}
/**
* Returns true if the node which may have side effects when executed.
*/
static boolean mayHaveSideEffects(Node n) {
return mayHaveSideEffects(n, null);
}
static boolean mayHaveSideEffects(Node n, AbstractCompiler compiler) {
return checkForStateChangeHelper(n, false, compiler);
}
/**
* Returns true if some node in n's subtree changes application state.
* If {@code checkForNewObjects} is true, we assume that newly created
* mutable objects (like object literals) change state. Otherwise, we assume
* that they have no side effects.
*/
private static boolean checkForStateChangeHelper(
Node n, boolean checkForNewObjects, AbstractCompiler compiler) {
// Rather than id which ops may have side effects, id the ones
// that we know to be safe
switch (n.getType()) {
// other side-effect free statements and expressions
case Token.AND:
case Token.BLOCK:
case Token.EXPR_RESULT:
case Token.HOOK:
case Token.IF:
case Token.IN:
case Token.PARAM_LIST:
case Token.NUMBER:
case Token.OR:
case Token.THIS:
case Token.TRUE:
case Token.FALSE:
case Token.NULL:
case Token.STRING:
case Token.STRING_KEY:
case Token.SWITCH:
case Token.TRY:
case Token.EMPTY:
break;
// Throws are by definition side effects
case Token.THROW:
return true;
case Token.OBJECTLIT:
if (checkForNewObjects) {
return true;
}
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
if (checkForStateChangeHelper(
c.getFirstChild(), checkForNewObjects, compiler)) {
return true;
}
}
return false;
case Token.ARRAYLIT:
case Token.REGEXP:
if (checkForNewObjects) {
return true;
}
break
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>;
case Token.VAR: // empty var statement (no declaration)
case Token.NAME: // variable by itself
if (n.getFirstChild() != null) {
return true;
}
break;
case Token.FUNCTION:
// Function expressions don't have side-effects, but function
// declarations change the namespace. Either way, we don't need to
// check the children, since they aren't executed at declaration time.
return checkForNewObjects || !isFunctionExpression(n);
case Token.NEW:
if (checkForNewObjects) {
return true;
}
if (!constructorCallHasSideEffects(n)) {
// loop below will see if the constructor parameters have
// side-effects
break;
}
return true;
case Token.CALL:
// calls to functions that have no side effects have the no
// side effect property set.
if (!functionCallHasSideEffects(n, compiler)) {
// loop below will see if the function parameters have
// side-effects
break;
}
return true;
default:
if (isSimpleOperatorType(n.getType())) {
break;
}
if (isAssignmentOp(n)) {
Node assignTarget = n.getFirstChild();
if (assignTarget.isName()) {
return true;
}
// Assignments will have side effects if
// a) The RHS has side effects, or
// b) The LHS has side effects, or
// c) A name on the LHS will exist beyond the life of this statement.
if (checkForStateChangeHelper(
n.getFirstChild(), checkForNewObjects, compiler) ||
checkForStateChangeHelper(
n.getLastChild(), checkForNewObjects, compiler)) {
return true;
}
if (isGet(assignTarget)) {
// If the object being assigned to is a local object, don't
// consider this a side-effect as it can't be referenced
// elsewhere. Don't do this recursively as the property might
// be an alias of another object, unlike a literal below.
Node current = assignTarget.getFirstChild();
if (evaluatesToLocalValue(current)) {
return false;
}
// A literal value as defined by "isLiteralValue" is guaranteed
// not to be an alias, or any components which are aliases of
// other objects.
// If the root object is a literal don't consider this a
// side-effect.
while (isGet(current)) {
current = current.
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>getFirstChild();
}
return !isLiteralValue(current, true);
} else {
// TODO(johnlenz): remove this code and make this an exception. This
// is here only for legacy reasons, the AST is not valid but
// preserve existing behavior.
return !isLiteralValue(assignTarget, true);
}
}
return true;
}
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
if (checkForStateChangeHelper(c, checkForNewObjects, compiler)) {
return true;
}
}
return false;
}
/**
* Do calls to this constructor have side effects?
*
* @param callNode - constructor call node
*/
static boolean constructorCallHasSideEffects(Node callNode) {
return constructorCallHasSideEffects(callNode, null);
}
static boolean constructorCallHasSideEffects(
Node callNode, AbstractCompiler compiler) {
if (!callNode.isNew()) {
throw new IllegalStateException(
"Expected NEW node, got " + Token.name(callNode.getType()));
}
if (callNode.isNoSideEffectsCall()) {
return false;
}
Node nameNode = callNode.getFirstChild();
if (nameNode.isName() &&
CONSTRUCTORS_WITHOUT_SIDE_EFFECTS.contains(nameNode.getString())) {
return false;
}
return true;
}
// A list of built-in object creation or primitive type cast functions that
// can also be called as constructors but lack side-effects.
// TODO(johnlenz): consider adding an extern annotation for this.
private static final Set<String> BUILTIN_FUNCTIONS_WITHOUT_SIDEEFFECTS =
ImmutableSet.of(
"Object", "Array", "String", "Number", "Boolean", "RegExp", "Error");
private static final Set<String> OBJECT_METHODS_WITHOUT_SIDEEFFECTS =
ImmutableSet.of("toString", "valueOf");
private static final Set<String> REGEXP_METHODS =
ImmutableSet.of("test", "exec");
private static final Set<String> STRING_REGEXP_METHODS =
ImmutableSet.of("match", "replace", "search", "split");
/**
* Returns true if calls to this function have side effects.
*
* @param callNode - function call node
*/
static boolean functionCallHasSideEffects(Node callNode) {
return functionCallHasSideEffects
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>(callNode, null);
}
/**
* Returns true if calls to this function have side effects.
*
* @param callNode The call node to inspected.
* @param compiler A compiler object to provide program state changing
* context information. Can be null.
*/
static boolean functionCallHasSideEffects(
Node callNode, @Nullable AbstractCompiler compiler) {
if (!callNode.isCall()) {
throw new IllegalStateException(
"Expected CALL node, got " + Token.name(callNode.getType()));
}
if (callNode.isNoSideEffectsCall()) {
return false;
}
Node nameNode = callNode.getFirstChild();
// Built-in functions with no side effects.
if (nameNode.isName()) {
String name = nameNode.getString();
if (BUILTIN_FUNCTIONS_WITHOUT_SIDEEFFECTS.contains(name)) {
return false;
}
} else if (nameNode.isGetProp()) {
if (callNode.hasOneChild()
&& OBJECT_METHODS_WITHOUT_SIDEEFFECTS.contains(
nameNode.getLastChild().getString())) {
return false;
}
if (callNode.isOnlyModifiesThisCall()
&& evaluatesToLocalValue(nameNode.getFirstChild())) {
return false;
}
// Math.floor has no side-effects.
// TODO(nicksantos): This is a terrible terrible hack, until
// I create a definitionProvider that understands namespacing.
if (nameNode.getFirstChild().isName()) {
if ("Math.floor".equals(nameNode.getQualifiedName())) {
return false;
}
}
if (compiler != null && !compiler.hasRegExpGlobalReferences()) {
if (nameNode.getFirstChild().isRegExp()
&& REGEXP_METHODS.contains(nameNode.getLastChild().getString())) {
return false;
} else if (nameNode.getFirstChild().isString()
&& STRING_REGEXP_METHODS.contains(
nameNode.getLastChild().getString())) {
Node param = nameNode.getNext();
if (param != null &&
(param.isString() || param.isRegExp()))
return false;
}
}
}
return true;
}
/**
* @return Whether the call has a local result.
*/
static boolean callHasLocalResult(Node n) {
Preconditions.checkState(n.isCall());
return (n.getSideEffectFlags()
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> & Node.FLAG_LOCAL_RESULTS) > 0;
}
/**
* @return Whether the new has a local result.
*/
static boolean newHasLocalResult(Node n) {
Preconditions.checkState(n.isNew());
return n.isOnlyModifiesThisCall();
}
/**
* Returns true if the current node's type implies side effects.
*
* This is a non-recursive version of the may have side effects
* check; used to check wherever the current node's type is one of
* the reason's why a subtree has side effects.
*/
static boolean nodeTypeMayHaveSideEffects(Node n) {
return nodeTypeMayHaveSideEffects(n, null);
}
static boolean nodeTypeMayHaveSideEffects(Node n, AbstractCompiler compiler) {
if (isAssignmentOp(n)) {
return true;
}
switch(n.getType()) {
case Token.DELPROP:
case Token.DEC:
case Token.INC:
case Token.THROW:
return true;
case Token.CALL:
return NodeUtil.functionCallHasSideEffects(n, compiler);
case Token.NEW:
return NodeUtil.constructorCallHasSideEffects(n, compiler);
case Token.NAME:
// A variable definition.
return n.hasChildren();
default:
return false;
}
}
/**
* @return Whether the tree can be affected by side-effects or
* has side-effects.
*/
static boolean canBeSideEffected(Node n) {
Set<String> emptySet = Collections.emptySet();
return canBeSideEffected(n, emptySet);
}
/**
* @param knownConstants A set of names known to be constant value at
* node 'n' (such as locals that are last written before n can execute).
* @return Whether the tree can be affected by side-effects or
* has side-effects.
*/
static boolean canBeSideEffected(Node n, Set<String> knownConstants) {
switch (n.getType()) {
case Token.CALL:
case Token.NEW:
// Function calls or constructor can reference changed values.
// TODO(johnlenz): Add some mechanism for determining that functions
// are unaffected by side effects.
return true;
case Token.NAME:
// Non-constant names values may have been changed.
return !isConstantName(n)
&& !knownConstants.contains(n.getString());
// Properties on constant
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> NAMEs can still be side-effected.
case Token.GETPROP:
case Token.GETELEM:
return true;
case Token.FUNCTION:
// Function expression are not changed by side-effects,
// and function declarations are not part of expressions.
Preconditions.checkState(isFunctionExpression(n));
return false;
}
for (Node c = n.getFirstChild(); c != null; c = c.getNext()) {
if (canBeSideEffected(c, knownConstants)) {
return true;
}
}
return false;
}
/*
* 0 comma ,
* 1 assignment = += -= *= /= %= <<= >>= >>>= &= ^= |=
* 2 conditional ?:
* 3 logical-or ||
* 4 logical-and &&
* 5 bitwise-or |
* 6 bitwise-xor ^
* 7 bitwise-and &
* 8 equality == !=
* 9 relational < <= > >=
* 10 bitwise shift << >> >>>
* 11 addition/subtraction + -
* 12 multiply/divide * / %
* 13 negation/increment ! ~ - ++ --
* 14 call, member () [] .
*/
static int precedence(int type) {
switch (type) {
case Token.COMMA: return 0;
case Token.ASSIGN_BITOR:
case Token.ASSIGN_BITXOR:
case Token.ASSIGN_BITAND:
case Token.ASSIGN_LSH:
case Token.ASSIGN_RSH:
case Token.ASSIGN_URSH:
case Token.ASSIGN_ADD:
case Token.ASSIGN_SUB:
case Token.ASSIGN_MUL:
case Token.ASSIGN_DIV:
case Token.ASSIGN_MOD:
case Token.ASSIGN: return 1;
case Token.HOOK: return 2; // ?: operator
case Token.OR: return 3;
case Token.AND: return 4;
case Token.BITOR: return 5;
case Token.BITXOR: return 6;
case Token.BITAND: return 7;
case Token.EQ:
case Token.NE:
case Token.SHEQ:
case Token.SHNE: return 8;
case Token.LT:
case Token.GT:
case Token.LE:
case Token.GE:
case Token.INSTANCEOF:
case Token.IN: return 9;
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> case Token.LSH:
case Token.RSH:
case Token.URSH: return 10;
case Token.SUB:
case Token.ADD: return 11;
case Token.MUL:
case Token.MOD:
case Token.DIV: return 12;
case Token.INC:
case Token.DEC:
case Token.NEW:
case Token.DELPROP:
case Token.TYPEOF:
case Token.VOID:
case Token.NOT:
case Token.BITNOT:
case Token.POS:
case Token.NEG: return 13;
case Token.CALL:
case Token.GETELEM:
case Token.GETPROP:
// Data values
case Token.ARRAYLIT:
case Token.EMPTY: // TODO(johnlenz): remove this.
case Token.FALSE:
case Token.FUNCTION:
case Token.NAME:
case Token.NULL:
case Token.NUMBER:
case Token.OBJECTLIT:
case Token.REGEXP:
case Token.STRING:
case Token.STRING_KEY:
case Token.THIS:
case Token.TRUE:
return 15;
default: throw new Error("Unknown precedence for " +
Token.name(type) +
" (type " + type + ")");
}
}
/**
* Apply the supplied predicate against
* all possible result Nodes of the expression.
*/
static boolean valueCheck(Node n, Predicate<Node> p) {
switch (n.getType()) {
case Token.ASSIGN:
case Token.COMMA:
return valueCheck(n.getLastChild(), p);
case Token.AND:
case Token.OR:
return valueCheck(n.getFirstChild(), p)
&& valueCheck(n.getLastChild(), p);
case Token.HOOK:
return valueCheck(n.getFirstChild().getNext(), p)
&& valueCheck(n.getLastChild(), p);
default:
return p.apply(n);
}
}
static class NumbericResultPredicate implements Predicate<Node> {
@Override
public boolean apply(Node n) {
return isNumericResultHelper(n);
}
}
static final NumbericResultPredicate NUMBERIC_RESULT_PREDICATE =
new NumbericResultPredicate();
/**
* Returns true if the result of node evaluation is always a number
*/
static boolean isNumericResult(Node n) {
return valueCheck(n, NUMBERIC_RESULT_PREDICATE);
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> }
static boolean isNumericResultHelper(Node n) {
switch (n.getType()) {
case Token.ADD:
return !mayBeString(n.getFirstChild())
&& !mayBeString(n.getLastChild());
case Token.BITNOT:
case Token.BITOR:
case Token.BITXOR:
case Token.BITAND:
case Token.LSH:
case Token.RSH:
case Token.URSH:
case Token.SUB:
case Token.MUL:
case Token.MOD:
case Token.DIV:
case Token.INC:
case Token.DEC:
case Token.POS:
case Token.NEG:
case Token.NUMBER:
return true;
case Token.NAME:
String name = n.getString();
if (name.equals("NaN")) {
return true;
}
if (name.equals("Infinity")) {
return true;
}
return false;
default:
return false;
}
}
static class BooleanResultPredicate implements Predicate<Node> {
@Override
public boolean apply(Node n) {
return isBooleanResultHelper(n);
}
}
static final BooleanResultPredicate BOOLEAN_RESULT_PREDICATE =
new BooleanResultPredicate();
/**
* @return Whether the result of node evaluation is always a boolean
*/
static boolean isBooleanResult(Node n) {
return valueCheck(n, BOOLEAN_RESULT_PREDICATE);
}
static boolean isBooleanResultHelper(Node n) {
switch (n.getType()) {
// Primitives
case Token.TRUE:
case Token.FALSE:
// Comparisons
case Token.EQ:
case Token.NE:
case Token.SHEQ:
case Token.SHNE:
case Token.LT:
case Token.GT:
case Token.LE:
case Token.GE:
// Queries
case Token.IN:
case Token.INSTANCEOF:
// Inversion
case Token.NOT:
// delete operator returns a boolean.
case Token.DELPROP:
return true;
default:
return false;
}
}
static boolean isUndefined(Node n) {
switch (n.getType()) {
case Token.VOID:
return true;
case Token.NAME:
return n.getString().equals("undefined");
}
return false;
}
static boolean isNullOrUndefined(Node n) {
return n.isNull() || isUndefined(n);
}
static class MayBeStringResult
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>Predicate implements Predicate<Node> {
@Override
public boolean apply(Node n) {
return mayBeStringHelper(n);
}
}
static final MayBeStringResultPredicate MAY_BE_STRING_PREDICATE =
new MayBeStringResultPredicate();
/**
* @returns Whether the results is possibly a string.
*/
static boolean mayBeString(Node n) {
return mayBeString(n, true);
}
static boolean mayBeString(Node n, boolean recurse) {
if (recurse) {
return valueCheck(n, MAY_BE_STRING_PREDICATE);
} else {
return mayBeStringHelper(n);
}
}
static boolean mayBeStringHelper(Node n) {
return !isNumericResult(n) && !isBooleanResult(n)
&& !isUndefined(n) && !n.isNull();
}
/**
* Returns true if the operator is associative.
* e.g. (a * b) * c = a * (b * c)
* Note: "+" is not associative because it is also the concatenation
* for strings. e.g. "a" + (1 + 2) is not "a" + 1 + 2
*/
static boolean isAssociative(int type) {
switch (type) {
case Token.MUL:
case Token.AND:
case Token.OR:
case Token.BITOR:
case Token.BITXOR:
case Token.BITAND:
return true;
default:
return false;
}
}
/**
* Returns true if the operator is commutative.
* e.g. (a * b) * c = c * (b * a)
* Note 1: "+" is not commutative because it is also the concatenation
* for strings. e.g. "a" + (1 + 2) is not "a" + 1 + 2
* Note 2: only operations on literals and pure functions are commutative.
*/
static boolean isCommutative(int type) {
switch (type) {
case Token.MUL:
case Token.BITOR:
case Token.BITXOR:
case Token.BITAND:
return true;
default:
return false;
}
}
static boolean isAssignmentOp(Node n) {
switch (n.getType()){
case Token.ASSIGN:
case Token.ASSIGN_BITOR:
case Token.
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>ASSIGN_BITXOR:
case Token.ASSIGN_BITAND:
case Token.ASSIGN_LSH:
case Token.ASSIGN_RSH:
case Token.ASSIGN_URSH:
case Token.ASSIGN_ADD:
case Token.ASSIGN_SUB:
case Token.ASSIGN_MUL:
case Token.ASSIGN_DIV:
case Token.ASSIGN_MOD:
return true;
}
return false;
}
static int getOpFromAssignmentOp(Node n) {
switch (n.getType()){
case Token.ASSIGN_BITOR:
return Token.BITOR;
case Token.ASSIGN_BITXOR:
return Token.BITXOR;
case Token.ASSIGN_BITAND:
return Token.BITAND;
case Token.ASSIGN_LSH:
return Token.LSH;
case Token.ASSIGN_RSH:
return Token.RSH;
case Token.ASSIGN_URSH:
return Token.URSH;
case Token.ASSIGN_ADD:
return Token.ADD;
case Token.ASSIGN_SUB:
return Token.SUB;
case Token.ASSIGN_MUL:
return Token.MUL;
case Token.ASSIGN_DIV:
return Token.DIV;
case Token.ASSIGN_MOD:
return Token.MOD;
}
throw new IllegalArgumentException("Not an assignment op:" + n);
}
/**
* Determines if the given node contains a function statement or function
* expression.
*/
static boolean containsFunction(Node n) {
return containsType(n, Token.FUNCTION);
}
/**
* Returns true if the shallow scope contains references to 'this' keyword
*/
static boolean referencesThis(Node n) {
Node start = (n.isFunction()) ? n.getLastChild() : n;
return containsType(start, Token.THIS, MATCH_NOT_FUNCTION);
}
/**
* Is this a GETPROP or GETELEM node?
*/
static boolean isGet(Node n) {
return n.isGetProp() || n.isGetElem();
}
/**
* Is this node the name of a variable being declared?
*
* @param n The node
* @return True if {@code n} is NAME and {@code parent} is VAR
*/
static boolean isVarDeclaration(Node n) {
// There is no need to verify that parent != null because a NAME node
// always has a parent in a valid parse tree.
return n.isName() && n.getParent().isVar();
}
/**
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> * For an assignment or variable declaration get the assigned value.
* @return The value node representing the new value.
*/
static Node getAssignedValue(Node n) {
Preconditions.checkState(n.isName());
Node parent = n.getParent();
if (parent.isVar()) {
return n.getFirstChild();
} else if (parent.isAssign() && parent.getFirstChild() == n) {
return n.getNext();
} else {
return null;
}
}
/**
* Is this node an assignment expression statement?
*
* @param n The node
* @return True if {@code n} is EXPR_RESULT and {@code n}'s
* first child is ASSIGN
*/
static boolean isExprAssign(Node n) {
return n.isExprResult()
&& n.getFirstChild().isAssign();
}
/**
* Is this node a call expression statement?
*
* @param n The node
* @return True if {@code n} is EXPR_RESULT and {@code n}'s
* first child is CALL
*/
static boolean isExprCall(Node n) {
return n.isExprResult()
&& n.getFirstChild().isCall();
}
/**
* @return Whether the node represents a FOR-IN loop.
*/
static boolean isForIn(Node n) {
return n.isFor()
&& n.getChildCount() == 3;
}
/**
* Determines whether the given node is a FOR, DO, or WHILE node.
*/
static boolean isLoopStructure(Node n) {
switch (n.getType()) {
case Token.FOR:
case Token.DO:
case Token.WHILE:
return true;
default:
return false;
}
}
/**
* @param n The node to inspect.
* @return If the node, is a FOR, WHILE, or DO, it returns the node for
* the code BLOCK, null otherwise.
*/
static Node getLoopCodeBlock(Node n) {
switch (n.getType()) {
case Token.FOR:
case Token.WHILE:
return n.getLastChild();
case Token.DO:
return n.getFirstChild();
default:
return null;
}
}
/**
* @return Whether the specified node has a loop parent that
* is within the current scope.
*/
static boolean isWithinLoop(Node n) {
for (Node parent : n.
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>getAncestors()) {
if (NodeUtil.isLoopStructure(parent)) {
return true;
}
if (parent.isFunction()) {
break;
}
}
return false;
}
/**
* Determines whether the given node is a FOR, DO, WHILE, WITH, or IF node.
*/
static boolean isControlStructure(Node n) {
switch (n.getType()) {
case Token.FOR:
case Token.DO:
case Token.WHILE:
case Token.WITH:
case Token.IF:
case Token.LABEL:
case Token.TRY:
case Token.CATCH:
case Token.SWITCH:
case Token.CASE:
case Token.DEFAULT_CASE:
return true;
default:
return false;
}
}
/**
* Determines whether the given node is code node for FOR, DO,
* WHILE, WITH, or IF node.
*/
static boolean isControlStructureCodeBlock(Node parent, Node n) {
switch (parent.getType()) {
case Token.FOR:
case Token.WHILE:
case Token.LABEL:
case Token.WITH:
return parent.getLastChild() == n;
case Token.DO:
return parent.getFirstChild() == n;
case Token.IF:
return parent.getFirstChild() != n;
case Token.TRY:
return parent.getFirstChild() == n || parent.getLastChild() == n;
case Token.CATCH:
return parent.getLastChild() == n;
case Token.SWITCH:
case Token.CASE:
return parent.getFirstChild() != n;
case Token.DEFAULT_CASE:
return true;
default:
Preconditions.checkState(isControlStructure(parent));
return false;
}
}
/**
* Gets the condition of an ON_TRUE / ON_FALSE CFG edge.
* @param n a node with an outgoing conditional CFG edge
* @return the condition node or null if the condition is not obviously a node
*/
static Node getConditionExpression(Node n) {
switch (n.getType()) {
case Token.IF:
case Token.WHILE:
return n.getFirstChild();
case Token.DO:
return n.getLastChild();
case Token.FOR:
switch (n.getChildCount()) {
case 3:
return null;
case 4:
return n.getFirstChild().getNext();
}
throw new IllegalArgumentException("malformed 'for' statement " + n);
case Token
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>.CASE:
return null;
}
throw new IllegalArgumentException(n + " does not have a condition.");
}
/**
* @return Whether the node is of a type that contain other statements.
*/
static boolean isStatementBlock(Node n) {
return n.isScript() || n.isBlock();
}
/**
* @return Whether the node is used as a statement.
*/
static boolean isStatement(Node n) {
return isStatementParent(n.getParent());
}
static boolean isStatementParent(Node parent) {
// It is not possible to determine definitely if a node is a statement
// or not if it is not part of the AST. A FUNCTION node can be
// either part of an expression or a statement.
Preconditions.checkState(parent != null);
switch (parent.getType()) {
case Token.SCRIPT:
case Token.BLOCK:
case Token.LABEL:
return true;
default:
return false;
}
}
/** Whether the node is part of a switch statement. */
static boolean isSwitchCase(Node n) {
return n.isCase() || n.isDefaultCase();
}
/**
* @return Whether the name is a reference to a variable, function or
* function parameter (not a label or a empty function expression name).
*/
static boolean isReferenceName(Node n) {
return n.isName() && !n.getString().isEmpty();
}
/** Whether the child node is the FINALLY block of a try. */
static boolean isTryFinallyNode(Node parent, Node child) {
return parent.isTry() && parent.getChildCount() == 3
&& child == parent.getLastChild();
}
/** Whether the node is a CATCH container BLOCK. */
static boolean isTryCatchNodeContainer(Node n) {
Node parent = n.getParent();
return parent.isTry()
&& parent.getFirstChild().getNext() == n;
}
/** Safely remove children while maintaining a valid node structure. */
static void removeChild(Node parent, Node node) {
if (isTryFinallyNode(parent, node)) {
if (NodeUtil.hasCatchHandler(getCatchBlock(parent))) {
// A finally can only be removed if there is a catch.
parent.removeChild(node);
} else {
// Otherwise, only its children can be removed.
node.detachChildren();
}
} else if (node.isCatch()) {
// The CATCH can can only
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> Preconditions.checkState(block.isBlock());
Node parent = block.getParent();
// Try to remove the block if its parent is a block/script or if its
// parent is label and it has exactly one child.
if (isStatementBlock(parent)) {
Node previous = block;
while (block.hasChildren()) {
Node child = block.removeFirstChild();
parent.addChildAfter(child, previous);
previous = child;
}
parent.removeChild(block);
return true;
} else {
return false;
}
}
/**
* @param node A node
* @return Whether the call is a NEW or CALL node.
*/
static boolean isCallOrNew(Node node) {
return node.isCall() || node.isNew();
}
/**
* Return a BLOCK node for the given FUNCTION node.
*/
static Node getFunctionBody(Node fn) {
Preconditions.checkArgument(fn.isFunction());
return fn.getLastChild();
}
/**
* Is this node or any of its children a CALL?
*/
static boolean containsCall(Node n) {
return containsType(n, Token.CALL);
}
/**
* Is this node a function declaration? A function declaration is a function
* that has a name that is added to the current scope (i.e. a function that
* is not part of a expression; see {@link #isFunctionExpression}).
*/
static boolean isFunctionDeclaration(Node n) {
return n.isFunction() && isStatement(n);
}
/**
* Is this node a hoisted function declaration? A function declaration in the
* scope root is hoisted to the top of the scope.
* See {@link #isFunctionDeclaration}).
*/
static boolean isHoistedFunctionDeclaration(Node n) {
return isFunctionDeclaration(n)
&& (n.getParent().isScript()
|| n.getParent().getParent().isFunction());
}
/**
* Is a FUNCTION node an function expression? An function expression is one
* that has either no name or a name that is not added to the current scope.
*
* <p>Some examples of function expressions:
* <pre>
* (function () {})
* (function f() {})()
* [ function f() {} ]
* var f = function f() {};
* for (function f() {};;) {}
* </pre>
*
* <p>Some examples of functions that are <em>not</em> expressions:
* <pre
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> }
/**
* Determines whether a node represents an object literal key
* (e.g. key1 in {key1: value1, key2: value2}).
*
* @param node A node
* @param parent The node's parent
*/
static boolean isObjectLitKey(Node node, Node parent) {
switch (node.getType()) {
case Token.STRING_KEY:
case Token.GETTER_DEF:
case Token.SETTER_DEF:
return true;
}
return false;
}
/**
* Get the name of an object literal key.
*
* @param key A node
*/
static String getObjectLitKeyName(Node key) {
switch (key.getType()) {
case Token.STRING_KEY:
case Token.GETTER_DEF:
case Token.SETTER_DEF:
return key.getString();
}
throw new IllegalStateException("Unexpected node type: " + key);
}
/**
* @param key A OBJECTLIT key node.
* @return The type expected when using the key.
*/
static JSType getObjectLitKeyTypeFromValueType(Node key, JSType valueType) {
if (valueType != null) {
switch (key.getType()) {
case Token.GETTER_DEF:
// GET must always return a function type.
if (valueType.isFunctionType()) {
FunctionType fntype = valueType.toMaybeFunctionType();
valueType = fntype.getReturnType();
} else {
return null;
}
break;
case Token.SETTER_DEF:
if (valueType.isFunctionType()) {
// SET must always return a function type.
FunctionType fntype = valueType.toMaybeFunctionType();
Node param = fntype.getParametersNode().getFirstChild();
// SET function must always have one parameter.
valueType = param.getJSType();
} else {
return null;
}
break;
}
}
return valueType;
}
/**
* Determines whether a node represents an object literal get or set key
* (e.g. key1 in {get key1() {}, set key2(a){}).
*
* @param node A node
*/
static boolean isGetOrSetKey(Node node) {
switch (node.getType()) {
case Token.GETTER_DEF:
case Token.SETTER_DEF:
return true;
}
return false;
}
/**
* Converts an operator's token value (see {@link
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> Token}) to a string
* representation.
*
* @param operator the operator's token value to convert
* @return the string representation or {@code null} if the token value is
* not an operator
*/
static String opToStr(int operator) {
switch (operator) {
case Token.BITOR: return "|";
case Token.OR: return "||";
case Token.BITXOR: return "^";
case Token.AND: return "&&";
case Token.BITAND: return "&";
case Token.SHEQ: return "===";
case Token.EQ: return "==";
case Token.NOT: return "!";
case Token.NE: return "!=";
case Token.SHNE: return "!==";
case Token.LSH: return "<<";
case Token.IN: return "in";
case Token.LE: return "<=";
case Token.LT: return "<";
case Token.URSH: return ">>>";
case Token.RSH: return ">>";
case Token.GE: return ">=";
case Token.GT: return ">";
case Token.MUL: return "*";
case Token.DIV: return "/";
case Token.MOD: return "%";
case Token.BITNOT: return "~";
case Token.ADD: return "+";
case Token.SUB: return "-";
case Token.POS: return "+";
case Token.NEG: return "-";
case Token.ASSIGN: return "=";
case Token.ASSIGN_BITOR: return "|=";
case Token.ASSIGN_BITXOR: return "^=";
case Token.ASSIGN_BITAND: return "&=";
case Token.ASSIGN_LSH: return "<<=";
case Token.ASSIGN_RSH: return ">>=";
case Token.ASSIGN_URSH: return ">>>=";
case Token.ASSIGN_ADD: return "+=";
case Token.ASSIGN_SUB: return "-=";
case Token.ASSIGN_MUL: return "*=";
case Token.ASSIGN_DIV: return "/=";
case Token.ASSIGN_MOD: return "%=";
case Token.VOID: return "void";
case Token.TYPEOF: return "typeof";
case Token.INSTANCEOF: return "instanceof";
default: return null;
}
}
/**
* Converts an operator's token value (see {@link Token}) to a string
* representation or fails.
*
* @param operator the operator's token value to convert
*
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> @return the string representation
* @throws Error if the token value is not an operator
*/
static String opToStrNoFail(int operator) {
String res = opToStr(operator);
if (res == null) {
throw new Error("Unknown op " + operator + ": " +
Token.name(operator));
}
return res;
}
/**
* @return true if n or any of its children are of the specified type
*/
static boolean containsType(Node node,
int type,
Predicate<Node> traverseChildrenPred) {
return has(node, new MatchNodeType(type), traverseChildrenPred);
}
/**
* @return true if n or any of its children are of the specified type
*/
static boolean containsType(Node node, int type) {
return containsType(node, type, Predicates.<Node>alwaysTrue());
}
/**
* Given a node tree, finds all the VAR declarations in that tree that are
* not in an inner scope. Then adds a new VAR node at the top of the current
* scope that redeclares them, if necessary.
*/
static void redeclareVarsInsideBranch(Node branch) {
Collection<Node> vars = getVarsDeclaredInBranch(branch);
if (vars.isEmpty()) {
return;
}
Node parent = getAddingRoot(branch);
for (Node nameNode : vars) {
Node var = IR.var(
IR.name(nameNode.getString())
.srcref(nameNode))
.srcref(nameNode);
copyNameAnnotations(nameNode, var.getFirstChild());
parent.addChildToFront(var);
}
}
/**
* Copy any annotations that follow a named value.
* @param source
* @param destination
*/
static void copyNameAnnotations(Node source, Node destination) {
if (source.getBooleanProp(Node.IS_CONSTANT_NAME)) {
destination.putBooleanProp(Node.IS_CONSTANT_NAME, true);
}
}
/**
* Gets a Node at the top of the current scope where we can add new var
* declarations as children.
*/
private static Node getAddingRoot(Node n) {
Node addingRoot = null;
Node ancestor = n;
while (null != (ancestor = ancestor.getParent())) {
int type = ancestor.getType();
if (type == Token.SCRIPT) {
addingRoot = ancestor;
break;
} else if (type == Token.FUNCTION) {
adding
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> based on call target.
*/
static Node newCallNode(Node callTarget, Node... parameters) {
boolean isFreeCall = !isGet(callTarget);
Node call = IR.call(callTarget);
call.putBooleanProp(Node.FREE_CALL, isFreeCall);
for (Node parameter : parameters) {
call.addChildToBack(parameter);
}
return call;
}
/**
* @return Whether the node is known to be a value that is not referenced
* elsewhere.
*/
static boolean evaluatesToLocalValue(Node value) {
return evaluatesToLocalValue(value, Predicates.<Node>alwaysFalse());
}
/**
* @param locals A predicate to apply to unknown local values.
* @return Whether the node is known to be a value that is not a reference
* outside the expression scope.
*/
static boolean evaluatesToLocalValue(Node value, Predicate<Node> locals) {
switch (value.getType()) {
case Token.ASSIGN:
// A result that is aliased by a non-local name, is the effectively the
// same as returning a non-local name, but this doesn't matter if the
// value is immutable.
return NodeUtil.isImmutableValue(value.getLastChild())
|| (locals.apply(value)
&& evaluatesToLocalValue(value.getLastChild(), locals));
case Token.COMMA:
return evaluatesToLocalValue(value.getLastChild(), locals);
case Token.AND:
case Token.OR:
return evaluatesToLocalValue(value.getFirstChild(), locals)
&& evaluatesToLocalValue(value.getLastChild(), locals);
case Token.HOOK:
return evaluatesToLocalValue(value.getFirstChild().getNext(), locals)
&& evaluatesToLocalValue(value.getLastChild(), locals);
case Token.INC:
case Token.DEC:
if (value.getBooleanProp(Node.INCRDECR_PROP)) {
return evaluatesToLocalValue(value.getFirstChild(), locals);
} else {
return true;
}
case Token.THIS:
return locals.apply(value);
case Token.NAME:
return isImmutableValue(value) || locals.apply(value);
case Token.GETELEM:
case Token.GETPROP:
// There is no information about the locality of object properties.
return locals.apply(value);
case Token.CALL:
return callHasLocalResult(value)
|| isToStringMethodCall(value)
|| locals.apply(value);
case Token.NEW:
return new
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>HasLocalResult(value)
|| locals.apply(value);
case Token.FUNCTION:
case Token.REGEXP:
case Token.ARRAYLIT:
case Token.OBJECTLIT:
// Literals objects with non-literal children are allowed.
return true;
case Token.DELPROP:
case Token.IN:
// TODO(johnlenz): should IN operator be included in #isSimpleOperator?
return true;
default:
// Other op force a local value:
// x = '' + g (x is now an local string)
// x -= g (x is now an local number)
if (isAssignmentOp(value)
|| isSimpleOperator(value)
|| isImmutableValue(value)) {
return true;
}
throw new IllegalStateException(
"Unexpected expression node" + value +
"\n parent:" + value.getParent());
}
}
/**
* Given the first sibling, this returns the nth
* sibling or null if no such sibling exists.
* This is like "getChildAtIndex" but returns null for non-existent indexes.
*/
private static Node getNthSibling(Node first, int index) {
Node sibling = first;
while (index != 0 && sibling != null) {
sibling = sibling.getNext();
index--;
}
return sibling;
}
/**
* Given the function, this returns the nth
* argument or null if no such parameter exists.
*/
static Node getArgumentForFunction(Node function, int index) {
Preconditions.checkState(function.isFunction());
return getNthSibling(
function.getFirstChild().getNext().getFirstChild(), index);
}
/**
* Given the new or call, this returns the nth
* argument of the call or null if no such argument exists.
*/
static Node getArgumentForCallOrNew(Node call, int index) {
Preconditions.checkState(isCallOrNew(call));
return getNthSibling(
call.getFirstChild().getNext(), index);
}
private static boolean isToStringMethodCall(Node call) {
Node getNode = call.getFirstChild();
if (isGet(getNode)) {
Node propNode = getNode.getLastChild();
return propNode.isString() && "toString".equals(propNode.getString());
}
return false;
}
/** Find the best JSDoc for the given node. */
static JSDocInfo getBestJSDocInfo(Node n) {
JSDocInfo info = n.getJSD
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>Value.getParent() == null) {
return null;
}
if (isObjectLitKey(lValue, lValue.getParent())) {
Node owner = getBestLValue(lValue.getParent());
if (owner != null) {
String ownerName = getBestLValueName(owner);
if (ownerName != null) {
return ownerName + "." + getObjectLitKeyName(lValue);
}
}
return null;
}
return lValue.getQualifiedName();
}
/**
* @returns false iff the result of the expression is not consumed.
*/
static boolean isExpressionResultUsed(Node expr) {
// TODO(johnlenz): consider sharing some code with trySimpleUnusedResult.
Node parent = expr.getParent();
switch (parent.getType()) {
case Token.EXPR_RESULT:
return false;
case Token.HOOK:
case Token.AND:
case Token.OR:
return (expr == parent.getFirstChild())
? true : isExpressionResultUsed(parent);
case Token.COMMA:
return (expr == parent.getFirstChild())
? false : isExpressionResultUsed(parent);
case Token.FOR:
if (!NodeUtil.isForIn(parent)) {
// Only an expression whose result is in the condition part of the
// expression is used.
return (parent.getChildAtIndex(1) == expr);
}
break;
}
return true;
}
/**
* @param n The expression to check.
* @return Whether the expression is unconditionally executed only once in the
* containing execution scope.
*/
static boolean isExecutedExactlyOnce(Node n) {
inspect: do {
Node parent = n.getParent();
switch (parent.getType()) {
case Token.IF:
case Token.HOOK:
case Token.AND:
case Token.OR:
if (parent.getFirstChild() != n) {
return false;
}
// other ancestors may be conditional
continue inspect;
case Token.FOR:
if (NodeUtil.isForIn(parent)) {
if (parent.getChildAtIndex(1) != n) {
return false;
}
} else {
if (parent.getFirstChild() != n) {
return false;
}
}
// other ancestors may be conditional
continue inspect;
case Token.WHILE:
case Token.DO:
return false;
case Token.TRY:
// Consider all code under a try/catch to be conditionally executed
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>.
if (!hasFinally(parent) || parent.getLastChild() != n) {
return false;
}
continue inspect;
case Token.CASE:
case Token.DEFAULT_CASE:
return false;
case Token.SCRIPT:
case Token.FUNCTION:
// Done, we've reached the scope root.
break inspect;
}
} while ((n = n.getParent()) != null);
return true;
}
static Node booleanNode(boolean value) {
return value ? IR.trueNode() : IR.falseNode();
}
static Node numberNode(double value, Node srcref) {
Node result;
if (Double.isNaN(value)) {
result = IR.name("NaN");
} else if (value == Double.POSITIVE_INFINITY) {
result = IR.name("Infinity");
} else if (value == Double.NEGATIVE_INFINITY) {
result = IR.neg(IR.name("Infinity"));
} else {
result = IR.number(value);
}
if (srcref != null) {
result.srcrefTree(srcref);
}
return result;
}
}
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>;
this.nameNode = nameNode;
this.type = type;
this.scope = scope;
this.index = index;
this.input = input;
this.isDefine = isDefine;
this.info = info;
this.typeInferred = inferred;
}
/**
* Gets the name of the variable.
*/
@Override
public String getName() {
return name;
}
/**
* Gets the node for the name of the variable.
*/
@Override
public Node getNode() {
return nameNode;
}
CompilerInput getInput() {
return input;
}
@Override
public StaticSourceFile getSourceFile() {
return nameNode.getStaticSourceFile();
}
@Override
public Var getSymbol() {
return this;
}
@Override
public Var getDeclaration() {
return nameNode == null ? null : this;
}
/**
* Gets the parent of the name node.
*/
public Node getParentNode() {
return nameNode == null ? null : nameNode.getParent();
}
/**
* Whether this is a bleeding function (an anonymous named function
* that bleeds into the inner scope.
*/
public boolean isBleedingFunction() {
return NodeUtil.isFunctionExpression(getParentNode());
}
/**
* Gets the scope where this variable is declared.
*/
Scope getScope() {
return scope;
}
/**
* Returns whether this is a global variable.
*/
public boolean isGlobal() {
return scope.isGlobal();
}
/**
* Returns whether this is a local variable.
*/
public boolean isLocal() {
return scope.isLocal();
}
/**
* Returns whether this is defined in an extern file.
*/
boolean isExtern() {
return input == null || input.isExtern();
}
/**
* Returns {@code true} if the variable is declared as a constant,
* based on the value reported by {@code NodeUtil}.
*/
public boolean isConst() {
return nameNode != null && NodeUtil.isConstantName(nameNode);
}
/**
* Returns {@code true} if the variable is declared as a define.
* A variable is a define if it is annotated by {@code @define}.
*/
public boolean isDefine() {
return isDefine;
}
public Node getInitialValue() {
Node parent = getParentNode();
int pType = parent.getType();
if (pType ==
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> Token.FUNCTION) {
return parent;
} else if (pType == Token.ASSIGN) {
return parent.getLastChild();
} else if (pType == Token.VAR) {
return nameNode.getFirstChild();
} else {
return null;
}
}
/**
* Gets this variable's type. To know whether this type has been inferred,
* see {@code #isTypeInferred()}.
*/
@Override
public JSType getType() {
return type;
}
/**
* Returns the name node that produced this variable.
*/
public Node getNameNode() {
return nameNode;
}
/**
* Gets the JSDocInfo for the variable.
*/
@Override
public JSDocInfo getJSDocInfo() {
return info;
}
/**
* Sets this variable's type.
* @throws IllegalStateException if the variable's type is not inferred
*/
void setType(JSType type) {
Preconditions.checkState(isTypeInferred());
this.type = type;
}
/**
* Resolve this variable's type.
*/
void resolveType(ErrorReporter errorReporter) {
if (type != null) {
type = type.resolve(errorReporter, scope);
}
}
/**
* Returns whether this variable's type is inferred. To get the variable's
* type, see {@link #getType()}.
*/
@Override
public boolean isTypeInferred() {
return typeInferred;
}
public String getInputName() {
if (input == null)
return "<non-file>";
else
return input.getName();
}
public boolean isNoShadow() {
if (info != null && info.isNoShadow()) {
return true;
} else {
return false;
}
}
@Override public boolean equals(Object other) {
if (!(other instanceof Var)) {
return false;
}
Var otherVar = (Var) other;
return otherVar.nameNode == nameNode;
}
@Override public int hashCode() {
return nameNode.hashCode();
}
@Override
public String toString() {
return "Scope.Var " + name + "{" + type + "}";
}
/** Record that this is escaped by an inner scope. */
void markEscaped() {
markedEscaped = true;
}
/**
* Whether this is escaped by an inner scope.
* Notice that not all scope creators record this information.
*/
boolean isMarked
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> final CharMatcher ID_MATCHER = CharMatcher.inRange('a', 'z').
or(CharMatcher.inRange('A', 'Z')).or(CharMatcher.anyOf("0123456789_."));
// Warnings and Errors.
static final DiagnosticType UNKNOWN_TWEAK_WARNING =
DiagnosticType.warning(
"JSC_UNKNOWN_TWEAK_WARNING",
"no tweak registered with ID {0}");
static final DiagnosticType TWEAK_MULTIPLY_REGISTERED_ERROR =
DiagnosticType.error(
"JSC_TWEAK_MULTIPLY_REGISTERED_ERROR",
"Tweak {0} has already been registered.");
static final DiagnosticType NON_LITERAL_TWEAK_ID_ERROR =
DiagnosticType.error(
"JSC_NON_LITERAL_TWEAK_ID_ERROR",
"tweak ID must be a string literal");
static final DiagnosticType INVALID_TWEAK_DEFAULT_VALUE_WARNING =
DiagnosticType.warning(
"JSC_INVALID_TWEAK_DEFAULT_VALUE_WARNING",
"tweak {0} registered with {1} must have a default value that is a " +
"literal of type {2}");
static final DiagnosticType NON_GLOBAL_TWEAK_INIT_ERROR =
DiagnosticType.error(
"JSC_NON_GLOBAL_TWEAK_INIT_ERROR",
"tweak declaration {0} must occur in the global scope");
static final DiagnosticType TWEAK_OVERRIDE_AFTER_REGISTERED_ERROR =
DiagnosticType.error(
"JSC_TWEAK_OVERRIDE_AFTER_REGISTERED_ERROR",
"Cannot override the default value of tweak {0} after it has been " +
"registered");
static final DiagnosticType TWEAK_WRONG_GETTER_TYPE_WARNING =
DiagnosticType.warning(
"JSC_TWEAK_WRONG_GETTER_TYPE_WARNING",
"tweak getter function {0} used for tweak registered using {1}");
static final DiagnosticType INVALID_TWEAK_ID_ERROR =
DiagnosticType.error(
"JSC_INVALID_TWEAK_ID_ERROR",
"tweak ID contains illegal characters. Only letters, numbers, _ " +
"and . are allowed");
/**
* An enum of goog.tweak functions.
*/
private static enum TweakFunction {
REGISTER_BOOLEAN("goog.tweak.registerBoolean", "boolean", Token.TRUE,
Token.
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>FALSE),
REGISTER_NUMBER("goog.tweak.registerNumber", "number", Token.NUMBER),
REGISTER_STRING("goog.tweak.registerString", "string", Token.STRING),
OVERRIDE_DEFAULT_VALUE("goog.tweak.overrideDefaultValue"),
GET_COMPILER_OVERRIDES("goog.tweak.getCompilerOverrides_"),
GET_BOOLEAN("goog.tweak.getBoolean", REGISTER_BOOLEAN),
GET_NUMBER("goog.tweak.getNumber", REGISTER_NUMBER),
GET_STRING("goog.tweak.getString", REGISTER_STRING);
final String name;
final String expectedTypeName;
final int validNodeTypeA;
final int validNodeTypeB;
final TweakFunction registerFunction;
TweakFunction(String name) {
this(name, null, Token.ERROR, Token.ERROR, null);
}
TweakFunction(String name, String expectedTypeName,
int validNodeTypeA) {
this(name, expectedTypeName, validNodeTypeA, Token.ERROR, null);
}
TweakFunction(String name, String expectedTypeName,
int validNodeTypeA, int validNodeTypeB) {
this(name, expectedTypeName, validNodeTypeA, validNodeTypeB, null);
}
TweakFunction(String name, TweakFunction registerFunction) {
this(name, null, Token.ERROR, Token.ERROR, registerFunction);
}
TweakFunction(String name, String expectedTypeName,
int validNodeTypeA, int validNodeTypeB,
TweakFunction registerFunction) {
this.name = name;
this.expectedTypeName = expectedTypeName;
this.validNodeTypeA = validNodeTypeA;
this.validNodeTypeB = validNodeTypeB;
this.registerFunction = registerFunction;
}
boolean isValidNodeType(int type) {
return type == validNodeTypeA || type == validNodeTypeB;
}
boolean isCorrectRegisterFunction(TweakFunction registerFunction) {
Preconditions.checkNotNull(registerFunction);
return this.registerFunction == registerFunction;
}
boolean isGetterFunction() {
return registerFunction != null;
}
String getName() {
return name;
}
String getExpectedTypeName() {
return expectedTypeName;
}
Node createDefaultValueNode() {
switch (this) {
case REGISTER_BOOLEAN:
return IR.falseNode();
case REGISTER_NUMBER:
return IR.number(0);
case REGISTER_STRING:
return IR.string("");
}
throw new IllegalStateException();
}
}
// A map of
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>
throw new IllegalStateException("unexpect prop id " + propType);
}
}
private static class NumberNode extends Node {
private static final long serialVersionUID = 1L;
NumberNode(double number) {
super(Token.NUMBER);
this.number = number;
}
public NumberNode(double number, int lineno, int charno) {
super(Token.NUMBER, lineno, charno);
this.number = number;
}
@Override
public double getDouble() {
return this.number;
}
@Override
public void setDouble(double d) {
this.number = d;
}
@Override
boolean isEquivalentTo(Node node, boolean compareJsType, boolean recurse) {
boolean equivalent = super.isEquivalentTo(node, compareJsType, recurse);
if (equivalent) {
double thisValue = getDouble();
double thatValue = ((NumberNode) node).getDouble();
if (thisValue == thatValue) {
// detect the difference between 0.0 and -0.0.
return (thisValue != 0.0) || (1/thisValue == 1/thatValue);
}
}
return false;
}
private double number;
}
private static class StringNode extends Node {
private static final long serialVersionUID = 1L;
StringNode(int type, String str) {
super(type);
if (null == str) {
throw new IllegalArgumentException("StringNode: str is null");
}
this.str = str;
}
StringNode(int type, String str, int lineno, int charno) {
super(type, lineno, charno);
if (null == str) {
throw new IllegalArgumentException("StringNode: str is null");
}
this.str = str;
}
/**
* returns the string content.
* @return non null.
*/
@Override
public String getString() {
return this.str;
}
/**
* sets the string content.
* @param str the new value. Non null.
*/
@Override
public void setString(String str) {
if (null == str) {
throw new IllegalArgumentException("StringNode: str is null");
}
this.str = str;
}
@Override
boolean isEquivalentTo(Node node, boolean compareJsType, boolean recurse) {
return (super.isEquivalentTo(node, compareJsType, recurse)
&& this.str.equals(((StringNode) node).
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>++) {
if (null != children[i - 1].next) {
// fail early on loops. implies same node in array twice
throw new IllegalArgumentException("duplicate child");
}
children[i - 1].next = children[i];
Preconditions.checkArgument(children[i - 1].parent == null);
children[i - 1].parent = this;
}
Preconditions.checkArgument(children[children.length - 1].parent == null);
children[children.length - 1].parent = this;
if (null != this.last.next) {
// fail early on loops. implies same node in array twice
throw new IllegalArgumentException("duplicate child");
}
}
}
public static Node newNumber(double number) {
return new NumberNode(number);
}
public static Node newNumber(double number, int lineno, int charno) {
return new NumberNode(number, lineno, charno);
}
public static Node newString(String str) {
return new StringNode(Token.STRING, str);
}
public static Node newString(int type, String str) {
return new StringNode(type, str);
}
public static Node newString(String str, int lineno, int charno) {
return new StringNode(Token.STRING, str, lineno, charno);
}
public static Node newString(int type, String str, int lineno, int charno) {
return new StringNode(type, str, lineno, charno);
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public boolean hasChildren() {
return first != null;
}
public Node getFirstChild() {
return first;
}
public Node getLastChild() {
return last;
}
public Node getNext() {
return next;
}
public Node getChildBefore(Node child) {
if (child == first) {
return null;
}
Node n = first;
while (n.next != child) {
n = n.next;
if (n == null) {
throw new RuntimeException("node is not a child");
}
}
return n;
}
public Node getChildAtIndex(int i) {
Node n = first;
while (i > 0) {
n = n.next;
i--;
}
return n;
}
public int getIndexOfChild(Node child)
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>Prop(propType) != 0;
}
/**
* Returns the integer value for the property, or 0 if the property
* is not defined.
*/
public int getIntProp(int propType) {
PropListItem item = lookupProperty(propType);
if (item == null) {
return 0;
}
return item.getIntValue();
}
public int getExistingIntProp(int propType) {
PropListItem item = lookupProperty(propType);
if (item == null) {
throw new IllegalStateException("missing prop: " + propType);
}
return item.getIntValue();
}
public void putProp(int propType, Object value) {
if (propType == SOURCENAME_PROP) {
putProp(
STATIC_SOURCE_FILE, new SimpleSourceFile((String) value, false));
return;
}
removeProp(propType);
if (value != null) {
propListHead = createProp(propType, value, propListHead);
}
}
public void putBooleanProp(int propType, boolean value) {
putIntProp(propType, value ? 1 : 0);
}
public void putIntProp(int propType, int value) {
removeProp(propType);
if (value != 0) {
propListHead = createProp(propType, value, propListHead);
}
}
PropListItem createProp(int propType, Object value, PropListItem next) {
return new ObjectPropListItem(propType, value, next);
}
PropListItem createProp(int propType, int value, PropListItem next) {
return new IntPropListItem(propType, value, next);
}
// Gets all the property types, in sorted order.
private int[] getSortedPropTypes() {
int count = 0;
for (PropListItem x = propListHead; x != null; x = x.getNext()) {
count++;
}
int[] keys = new int[count];
for (PropListItem x = propListHead; x != null; x = x.getNext()) {
count--;
keys[count] = x.getType();
}
Arrays.sort(keys);
return keys;
}
/** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */
public double getDouble() throws UnsupportedOperationException {
if (this.getType() == Token.NUMBER) {
throw new IllegalStateException(
"Number node not created with
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> Node.newNumber");
} else {
throw new UnsupportedOperationException(this + " is not a number node");
}
}
/** Can only be called when <tt>getType() == TokenStream.NUMBER</tt> */
public void setDouble(double s) throws UnsupportedOperationException {
if (this.getType() == Token.NUMBER) {
throw new IllegalStateException(
"Number node not created with Node.newNumber");
} else {
throw new UnsupportedOperationException(this + " is not a string node");
}
}
/** Can only be called when node has String context. */
public String getString() throws UnsupportedOperationException {
if (this.getType() == Token.STRING) {
throw new IllegalStateException(
"String node not created with Node.newString");
} else {
throw new UnsupportedOperationException(this + " is not a string node");
}
}
/** Can only be called when node has String context. */
public void setString(String s) throws UnsupportedOperationException {
if (this.getType() == Token.STRING) {
throw new IllegalStateException(
"String node not created with Node.newString");
} else {
throw new UnsupportedOperationException(this + " is not a string node");
}
}
@Override
public String toString() {
return toString(true, true, true);
}
public String toString(
boolean printSource,
boolean printAnnotations,
boolean printType) {
StringBuilder sb = new StringBuilder();
toString(sb, printSource, printAnnotations, printType);
return sb.toString();
}
private void toString(
StringBuilder sb,
boolean printSource,
boolean printAnnotations,
boolean printType) {
sb.append(Token.name(type));
if (this instanceof StringNode) {
sb.append(' ');
sb.append(getString());
} else if (type == Token.FUNCTION) {
sb.append(' ');
// In the case of JsDoc trees, the first child is often not a string
// which causes exceptions to be thrown when calling toString or
// toStringTree.
if (first == null || first.getType() != Token.NAME) {
sb.append("<invalid>");
} else {
sb.append(first.getString());
}
} else if (type == Token.NUMBER) {
sb.append(' ');
sb.append(getDouble());
}
if (printSource) {
int lineno = getLineno();
if (lineno != -1) {
sb.append(' ');
sb.append(lineno);
}
}
if
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>
for (n = first, n2 = node2.first;
res == null && n != null;
n = n.next, n2 = n2.next) {
res = n.checkTreeTypeAwareEqualsImpl(n2);
if (res != null) {
return res;
}
}
return res;
}
/** Returns true if this node is equivalent semantically to another */
public boolean isEquivalentTo(Node node) {
return isEquivalentTo(node, false, true);
}
/**
* Returns true if this node is equivalent semantically to another and
* the types are equivalent.
*/
public boolean isEquivalentToTyped(Node node) {
return isEquivalentTo(node, true, true);
}
/**
* @param compareJsType Whether to compare the JSTypes of the nodes.
* @param recurse Whether to compare the children of the current node, if
* not only the the count of the children are compared.
* @return Whether this node is equivalent semantically to the provided node.
*/
boolean isEquivalentTo(Node node, boolean compareJsType, boolean recurse) {
if (type != node.getType()
|| getChildCount() != node.getChildCount()
|| this.getClass() != node.getClass()) {
return false;
}
if (compareJsType && !JSType.isEquivalent(jsType, node.getJSType())) {
return false;
}
if (type == Token.INC || type == Token.DEC) {
int post1 = this.getIntProp(INCRDECR_PROP);
int post2 = node.getIntProp(INCRDECR_PROP);
if (post1 != post2) {
return false;
}
} else if (type == Token.STRING || type == Token.STRING_KEY) {
if (type == Token.STRING_KEY) {
int quoted1 = this.getIntProp(QUOTED_PROP);
int quoted2 = node.getIntProp(QUOTED_PROP);
if (quoted1 != quoted2) {
return false;
}
}
int slashV1 = this.getIntProp(SLASH_V);
int slashV2 = node.getIntProp(SLASH_V);
if (slashV1 != slashV2) {
return false;
}
} else if (type == Token.CALL) {
if (this.getBooleanProp(FREE_CALL) != node.getBooleanProp(FREE_CALL)) {
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>
return false;
}
}
if (recurse) {
Node n, n2;
for (n = first, n2 = node.first;
n != null;
n = n.next, n2 = n2.next) {
if (!n.isEquivalentTo(n2, compareJsType, true)) {
return false;
}
}
}
return true;
}
/**
* This function takes a set of GETPROP nodes and produces a string that is
* each property separated by dots. If the node ultimately under the left
* sub-tree is not a simple name, this is not a valid qualified name.
*
* @return a null if this is not a qualified name, or a dot-separated string
* of the name and properties.
*/
public String getQualifiedName() {
if (type == Token.NAME) {
return getString();
} else if (type == Token.GETPROP) {
String left = getFirstChild().getQualifiedName();
if (left == null) {
return null;
}
return left + "." + getLastChild().getString();
} else if (type == Token.THIS) {
return "this";
} else {
return null;
}
}
/**
* Returns whether a node corresponds to a simple or a qualified name, such as
* <code>x</code> or <code>a.b.c</code> or <code>this.a</code>.
*/
public boolean isQualifiedName() {
switch (getType()) {
case Token.NAME:
case Token.THIS:
return true;
case Token.GETPROP:
return getFirstChild().isQualifiedName();
default:
return false;
}
}
/**
* Returns whether a node corresponds to a simple or a qualified name without
* a "this" reference, such as <code>a.b.c</code>, but not <code>this.a</code>
* .
*/
public boolean isUnscopedQualifiedName() {
switch (getType()) {
case Token.NAME:
return true;
case Token.GETPROP:
return getFirstChild().isUnscopedQualifiedName();
default:
return false;
}
}
// ==========================================================================
// Mutators
/**
* Removes this node from its parent. Equivalent to:
* node.getParent().removeChild();
*/
public Node detachFromParent() {
Preconditions.checkState(parent != null);
parent.removeChild(this);
return
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> function or constructor call's side effect flags.
* This property is only meaningful for {@link Token#CALL} and
* {@link Token#NEW} nodes.
*/
public void setSideEffectFlags(int flags) {
Preconditions.checkArgument(
getType() == Token.CALL || getType() == Token.NEW,
"setIsNoSideEffectsCall only supports CALL and NEW nodes, got " +
Token.name(getType()));
putIntProp(SIDE_EFFECT_FLAGS, flags);
}
public void setSideEffectFlags(SideEffectFlags flags) {
setSideEffectFlags(flags.valueOf());
}
/**
* Returns the side effects flags for this node.
*/
public int getSideEffectFlags() {
return getIntProp(SIDE_EFFECT_FLAGS);
}
/**
* A helper class for getting and setting the side-effect flags.
* @author johnlenz@google.com (John Lenz)
*/
public static class SideEffectFlags {
private int value = Node.SIDE_EFFECTS_ALL;
public SideEffectFlags() {
}
public SideEffectFlags(int value) {
this.value = value;
}
public int valueOf() {
return value;
}
/** All side-effect occur and the returned results are non-local. */
public void setAllFlags() {
value = Node.SIDE_EFFECTS_ALL;
}
/** No side-effects occur and the returned results are local. */
public void clearAllFlags() {
value = Node.NO_SIDE_EFFECTS | Node.FLAG_LOCAL_RESULTS;
}
public boolean areAllFlagsSet() {
return value == Node.SIDE_EFFECTS_ALL;
}
/**
* Preserve the return result flag, but clear the others:
* no global state change, no throws, no this change, no arguments change
*/
public void clearSideEffectFlags() {
value |= Node.NO_SIDE_EFFECTS;
}
public void setMutatesGlobalState() {
// Modify global means everything must be assumed to be modified.
removeFlag(Node.FLAG_GLOBAL_STATE_UNMODIFIED);
removeFlag(Node.FLAG_ARGUMENTS_UNMODIFIED);
removeFlag(Node.FLAG_THIS_UNMODIFIED);
}
public void setThrows() {
removeFlag(Node.FLAG_NO_THROWS);
}
public void setMutatesThis() {
removeFlag(Node.FLAG_THIS_UNMODIFIED);
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>/
public boolean isAdd() {
return this.getType() == Token.ADD;
}
public boolean isAnd() {
return this.getType() == Token.AND;
}
public boolean isArrayLit() {
return this.getType() == Token.ARRAYLIT;
}
public boolean isAssign() {
return this.getType() == Token.ASSIGN;
}
public boolean isAssignAdd() {
return this.getType() == Token.ASSIGN_ADD;
}
public boolean isBlock() {
return this.getType() == Token.BLOCK;
}
public boolean isBreak() {
return this.getType() == Token.BREAK;
}
public boolean isCall() {
return this.getType() == Token.CALL;
}
public boolean isCase() {
return this.getType() == Token.CASE;
}
public boolean isCatch() {
return this.getType() == Token.CATCH;
}
public boolean isComma() {
return this.getType() == Token.COMMA;
}
public boolean isContinue() {
return this.getType() == Token.CONTINUE;
}
public boolean isDebugger() {
return this.getType() == Token.DEBUGGER;
}
public boolean isDec() {
return this.getType() == Token.DEC;
}
public boolean isDefaultCase() {
return this.getType() == Token.DEFAULT_CASE;
}
public boolean isDelProp() {
return this.getType() == Token.DELPROP;
}
public boolean isDo() {
return this.getType() == Token.DO;
}
public boolean isEmpty() {
return this.getType() == Token.EMPTY;
}
public boolean isExprResult() {
return this.getType() == Token.EXPR_RESULT;
}
public boolean isFalse() {
return this.getType() == Token.FALSE;
}
public boolean isFor() {
return this.getType() == Token.FOR;
}
public boolean isFunction() {
return this.getType() == Token.FUNCTION;
}
public boolean isGetterDef() {
return this.getType() == Token.GETTER_DEF;
}
public boolean isGetElem() {
return this.getType() == Token.GETELEM;
}
public boolean isGetProp() {
return this.getType() == Token.GETPROP;
}
public boolean isHook() {
return this.getType() == Token.HOOK;
}
public boolean isIf() {
return this.getType
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>() == Token.IF;
}
public boolean isIn() {
return this.getType() == Token.IN;
}
public boolean isInc() {
return this.getType() == Token.INC;
}
public boolean isInstanceOf() {
return this.getType() == Token.INSTANCEOF;
}
public boolean isLabel() {
return this.getType() == Token.LABEL;
}
public boolean isLabelName() {
return this.getType() == Token.LABEL_NAME;
}
public boolean isName() {
return this.getType() == Token.NAME;
}
public boolean isNE() {
return this.getType() == Token.NE;
}
public boolean isNew() {
return this.getType() == Token.NEW;
}
public boolean isNot() {
return this.getType() == Token.NOT;
}
public boolean isNull() {
return this.getType() == Token.NULL;
}
public boolean isNumber() {
return this.getType() == Token.NUMBER;
}
public boolean isObjectLit() {
return this.getType() == Token.OBJECTLIT;
}
public boolean isOr() {
return this.getType() == Token.OR;
}
public boolean isParamList() {
return this.getType() == Token.PARAM_LIST;
}
public boolean isRegExp() {
return this.getType() == Token.REGEXP;
}
public boolean isReturn() {
return this.getType() == Token.RETURN;
}
public boolean isScript() {
return this.getType() == Token.SCRIPT;
}
public boolean isSetterDef() {
return this.getType() == Token.SETTER_DEF;
}
public boolean isString() {
return this.getType() == Token.STRING;
}
public boolean isStringKey() {
return this.getType() == Token.STRING_KEY;
}
public boolean isSwitch() {
return this.getType() == Token.SWITCH;
}
public boolean isThis() {
return this.getType() == Token.THIS;
}
public boolean isThrow() {
return this.getType() == Token.THROW;
}
public boolean isTrue() {
return this.getType() == Token.TRUE;
}
public boolean isTry() {
return this.getType() == Token.TRY;
}
public boolean isTypeOf() {
return this.getType() == Token.TYPEOF;
}
public boolean isVar() {
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> return this.getType() == Token.VAR;
}
public boolean isVoid() {
return this.getType() == Token.VOID;
}
public boolean isWhile() {
return this.getType() == Token.WHILE;
}
public boolean isWith() {
return this.getType() == Token.WITH;
}
}
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> convention){
this.convention = convention;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
switch (n.getType()) {
case Token.CALL:
String providedClassName =
codingConvention.extractClassNameIfProvide(n, parent);
if (providedClassName != null) {
provides.put(providedClassName, n);
}
break;
case Token.FUNCTION:
visitFunctionNode(n, parent);
break;
case Token.SCRIPT:
visitScriptNode(t, n);
}
}
private void visitFunctionNode(Node n, Node parent) {
Node name = null;
JSDocInfo info = parent.getJSDocInfo();
if (info != null && info.isConstructor()) {
name = parent.getFirstChild();
} else {
// look to the child, maybe it's a named function
info = n.getJSDocInfo();
if (info != null && info.isConstructor()) {
name = n.getFirstChild();
}
}
if (name != null && name.isQualifiedName()) {
String qualifiedName = name.getQualifiedName();
if (!this.convention.isPrivate(qualifiedName)) {
Visibility visibility = info.getVisibility();
if (!visibility.equals(JSDocInfo.Visibility.PRIVATE)) {
ctors.put(qualifiedName, name);
}
}
}
}
private void visitScriptNode(NodeTraversal t, Node n) {
for (Map.Entry<String, Node> ctorEntry : ctors.entrySet()) {
String ctor = ctorEntry.getKey();
int index = -1;
boolean found = false;
do {
index = ctor.indexOf('.', index +1);
String provideKey = index == -1 ? ctor : ctor.substring(0, index);
if (provides.containsKey(provideKey)) {
found = true;
break;
}
} while (index != -1);
if (!found) {
compiler.report(
t.makeError(ctorEntry.getValue(), checkLevel,
MISSING_PROVIDE_WARNING, ctorEntry.getKey()));
}
}
provides.clear();
ctors.clear();
}
}
}
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> Creates an interface function type.
* @param name the function's name
* @param source the node defining this function. Its type
* ({@link Node#getType()}) must be {@link Token#FUNCTION}.
*/
public FunctionType createInterfaceType(String name, Node source) {
return FunctionType.forInterface(this, name, source);
}
/**
* Creates a parameterized type.
*/
public ParameterizedType createParameterizedType(
ObjectType objectType, JSType parameterType) {
return new ParameterizedType(this, objectType, parameterType);
}
/**
* Creates a named type.
*/
@VisibleForTesting
public JSType createNamedType(String reference,
String sourceName, int lineno, int charno) {
return new NamedType(this, reference, sourceName, lineno, charno);
}
/**
* Identifies the name of a typedef or enum before we actually declare it.
*/
public void identifyNonNullableName(String name) {
Preconditions.checkNotNull(name);
nonNullableTypeNames.add(name);
}
/**
* Creates a JSType from the nodes representing a type.
* @param n The node with type info.
* @param sourceName The source file name.
* @param scope A scope for doing type name lookups.
*/
public JSType createFromTypeNodes(Node n, String sourceName,
StaticScope<JSType> scope) {
if (resolveMode == ResolveMode.LAZY_EXPRESSIONS) {
// If the type expression doesn't contain any names, just
// resolve it anyway.
boolean hasNames = hasTypeName(n);
if (hasNames) {
return new UnresolvedTypeExpression(this, n, sourceName);
}
}
return createFromTypeNodesInternal(n, sourceName, scope);
}
private boolean hasTypeName(Node n) {
if (n.getType() == Token.STRING) {
return true;
}
for (Node child = n.getFirstChild();
child != null; child = child.getNext()) {
if (hasTypeName(child)) {
return true;
}
}
return false;
}
/** @see #createFromTypeNodes(Node, String, StaticScope) */
private JSType createFromTypeNodesInternal(Node n, String sourceName,
StaticScope<JSType> scope) {
switch (n.getType()) {
case Token.LC: // Record type.
return createRecordTypeFromNodes(
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> n.getFirstChild(), sourceName, scope);
case Token.BANG: // Not nullable
return createFromTypeNodesInternal(
n.getFirstChild(), sourceName, scope)
.restrictByNotNullOrUndefined();
case Token.QMARK: // Nullable or unknown
Node firstChild = n.getFirstChild();
if (firstChild == null) {
return getNativeType(UNKNOWN_TYPE);
}
return createDefaultObjectUnion(
createFromTypeNodesInternal(
firstChild, sourceName, scope));
case Token.EQUALS: // Optional
return createOptionalType(
createFromTypeNodesInternal(
n.getFirstChild(), sourceName, scope));
case Token.ELLIPSIS: // Var args
return createOptionalType(
createFromTypeNodesInternal(
n.getFirstChild(), sourceName, scope));
case Token.STAR: // The AllType
return getNativeType(ALL_TYPE);
case Token.LB: // Array type
// TODO(nicksantos): Enforce membership restrictions on the Array.
return getNativeType(ARRAY_TYPE);
case Token.PIPE: // Union type
UnionTypeBuilder builder = new UnionTypeBuilder(this);
for (Node child = n.getFirstChild(); child != null;
child = child.getNext()) {
builder.addAlternate(
createFromTypeNodesInternal(child, sourceName, scope));
}
return builder.build();
case Token.EMPTY: // When the return value of a function is not specified
return getNativeType(UNKNOWN_TYPE);
case Token.VOID: // Only allowed in the return value of a function.
return getNativeType(VOID_TYPE);
case Token.STRING:
JSType namedType = getType(scope, n.getString(), sourceName,
n.getLineno(), n.getCharno());
if (resolveMode != ResolveMode.LAZY_NAMES) {
namedType = namedType.resolveInternal(reporter, scope);
}
if ((namedType instanceof ObjectType) &&
!(nonNullableTypeNames.contains(n.getString()))) {
Node typeList = n.getFirstChild();
if (typeList != null &&
("Array".equals(n.getString()) ||
"Object".equals(n.getString()))) {
JSType parameterType =
createFromTypeNodesInternal(
typeList.getLastChild(), sourceName, scope);
namedType = new ParameterizedType(
this, (ObjectType) namedType, parameterType);
if (typeList.hasMoreThanOneChild()) {
JS
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>Type indexType =
createFromTypeNodesInternal(
typeList.getFirstChild(), sourceName, scope);
namedType = new IndexedType(
this, (ObjectType) namedType, indexType);
}
}
return createDefaultObjectUnion(namedType);
} else {
return namedType;
}
case Token.FUNCTION:
ObjectType thisType = null;
boolean isConstructor = false;
Node current = n.getFirstChild();
if (current.getType() == Token.THIS ||
current.getType() == Token.NEW) {
Node contextNode = current.getFirstChild();
thisType =
ObjectType.cast(
createFromTypeNodesInternal(
contextNode, sourceName, scope)
.restrictByNotNullOrUndefined());
if (thisType == null) {
reporter.warning(
ScriptRuntime.getMessage0(
current.getType() == Token.THIS ?
"msg.jsdoc.function.thisnotobject" :
"msg.jsdoc.function.newnotobject"),
sourceName,
contextNode.getLineno(), contextNode.getCharno());
}
isConstructor = current.getType() == Token.NEW;
current = current.getNext();
}
FunctionParamBuilder paramBuilder = new FunctionParamBuilder(this);
if (current.getType() == Token.PARAM_LIST) {
Node args = current.getFirstChild();
for (Node arg = current.getFirstChild(); arg != null;
arg = arg.getNext()) {
if (arg.getType() == Token.ELLIPSIS) {
if (arg.getChildCount() == 0) {
paramBuilder.addVarArgs(getNativeType(UNKNOWN_TYPE));
} else {
paramBuilder.addVarArgs(
createFromTypeNodesInternal(
arg.getFirstChild(), sourceName, scope));
}
} else {
JSType type = createFromTypeNodesInternal(
arg, sourceName, scope);
if (arg.getType() == Token.EQUALS) {
boolean addSuccess = paramBuilder.addOptionalParams(type);
if (!addSuccess) {
reporter.warning(
ScriptRuntime.getMessage0("msg.jsdoc.function.varargs"),
sourceName, arg.getLineno(), arg.getCharno());
}
} else {
paramBuilder.addRequiredParams(type);
}
}
}
current = current.getNext();
}
JSType returnType =
createFromTypeNodesInternal(current, sourceName, scope);
return new FunctionBuilder
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>(this)
.withParams(paramBuilder)
.withReturnType(returnType)
.withTypeOfThis(thisType)
.setIsConstructor(isConstructor)
.build();
}
throw new IllegalStateException(
"Unexpected node in type expression: " + n.toString());
}
/**
* Creates a RecordType from the nodes representing said record type.
* @param n The node with type info.
* @param sourceName The source file name.
* @param scope A scope for doing type name lookups.
*/
private JSType createRecordTypeFromNodes(Node n, String sourceName,
StaticScope<JSType> scope) {
RecordTypeBuilder builder = new RecordTypeBuilder(this);
// For each of the fields in the record type.
for (Node fieldTypeNode = n.getFirstChild();
fieldTypeNode != null;
fieldTypeNode = fieldTypeNode.getNext()) {
// Get the property's name.
Node fieldNameNode = fieldTypeNode;
boolean hasType = false;
if (fieldTypeNode.getType() == Token.COLON) {
fieldNameNode = fieldTypeNode.getFirstChild();
hasType = true;
}
String fieldName = fieldNameNode.getString();
// TODO(user): Move this into the lexer/parser.
// Remove the string literal characters around a field name,
// if any.
if (fieldName.startsWith("'") || fieldName.startsWith("\"")) {
fieldName = fieldName.substring(1, fieldName.length() - 1);
}
// Get the property's type.
JSType fieldType = null;
if (hasType) {
// We have a declared type.
fieldType = createFromTypeNodesInternal(
fieldTypeNode.getLastChild(), sourceName, scope);
} else {
// Otherwise, the type is UNKNOWN.
fieldType = getNativeType(JSTypeNative.UNKNOWN_TYPE);
}
// Add the property to the record.
if (builder.addProperty(fieldName, fieldType, fieldNameNode) == null) {
// Duplicate field name, warning and skip
reporter.warning(
"Duplicate record field " + fieldName,
sourceName,
n.getLineno(), fieldNameNode.getCharno());
}
}
return builder.build();
}
/**
* Sets the template type name.
*/
public void setTemplateTypeName(String name) {
templateTypeName = name;
templateType = new TemplateType(this, name);
}
/**
* Clears the template type name.
*/
public void clearTemplateTypeName() {
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> case Token.BLOCK:
case Token.SCRIPT:
case Token.TRY:
return true;
case Token.FUNCTION:
// A function node represents the start of a function where the name
// is bleed into the local scope and parameters has been assigned
// to the formal argument names. The node includes the name of the
// function and the LP list since we assume the whole set up process
// is atomic without change in control flow. The next change of
// control is going into the function's body represent by the second
// child.
return n != parent.getFirstChild().getNext();
case Token.WHILE:
case Token.DO:
case Token.IF:
// Theses control structure is represented by its node that holds the
// condition. Each of them is a branch node based on its condition.
return NodeUtil.getConditionExpression(parent) != n;
case Token.FOR:
// The FOR(;;) node differs from other control structure in that
// it has a initialization and a increment statement. Those
// two statements have its corresponding CFG nodes to represent them.
// The FOR node represents the condition check for each iteration.
// That way the following:
// for(var x = 0; x < 10; x++) { } has a graph that is isomorphic to
// var x = 0; while(x<10) { x++; }
if (NodeUtil.isForIn(parent)) {
// TODO(user): Investigate how we should handle the case where
// we have a very complex expression inside the FOR-IN header.
return n != parent.getFirstChild();
} else {
return NodeUtil.getConditionExpression(parent) != n;
}
case Token.SWITCH:
case Token.CASE:
case Token.CATCH:
case Token.WITH:
return n != parent.getFirstChild();
default:
return false;
}
}
}
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>, TYPE_ERROR_FUNCTION_TYPE);
declareNativeFunctionType(s, URI_ERROR_FUNCTION_TYPE);
declareNativeValueType(s, "undefined", VOID_TYPE);
// ActiveXObject is uniquely special, because it can be used to construct
// any type (the type that it creates is related to the arguments you
// pass to it).
declareNativeValueType(s, "ActiveXObject", NO_OBJECT_TYPE);
return s;
}
private void declareNativeFunctionType(Scope scope, JSTypeNative tId) {
FunctionType t = typeRegistry.getNativeFunctionType(tId);
declareNativeType(scope, t.getInstanceType().getReferenceName(), t);
declareNativeType(
scope, t.getPrototype().getReferenceName(), t.getPrototype());
}
private void declareNativeValueType(Scope scope, String name,
JSTypeNative tId) {
declareNativeType(scope, name, typeRegistry.getNativeType(tId));
}
private void declareNativeType(Scope scope, String name, JSType t) {
scope.declare(name, null, t, null, false);
}
private static class DiscoverEnumsAndTypedefs
extends AbstractShallowStatementCallback {
private final JSTypeRegistry registry;
DiscoverEnumsAndTypedefs(JSTypeRegistry registry) {
this.registry = registry;
}
@Override
public void visit(NodeTraversal t, Node node, Node parent) {
Node nameNode = null;
switch (node.getType()) {
case Token.VAR:
for (Node child = node.getFirstChild();
child != null; child = child.getNext()) {
identifyNameNode(
child, child.getFirstChild(),
NodeUtil.getBestJSDocInfo(child));
}
break;
case Token.EXPR_RESULT:
Node firstChild = node.getFirstChild();
if (firstChild.isAssign()) {
identifyNameNode(
firstChild.getFirstChild(), firstChild.getLastChild(),
firstChild.getJSDocInfo());
} else {
identifyNameNode(
firstChild, null, firstChild.getJSDocInfo());
}
break;
}
}
private void identifyNameNode(
Node nameNode, Node valueNode, JSDocInfo info) {
if (nameNode.isQualifiedName()) {
if (info != null) {
if (info.hasEnumParameterType()) {
registry.identifyNonNullableName(nameNode.getQualifiedName());
} else if (info.has
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>Id();
if (n.isFunction() ||
n.isScript()) {
Preconditions.checkNotNull(inputId);
sourceName = NodeUtil.getSourceName(n);
}
// We do want to traverse the name of a named function, but we don't
// want to traverse the arguments or body.
boolean descend = parent == null || !parent.isFunction() ||
n == parent.getFirstChild() || parent == scope.getRootNode();
if (descend) {
// Handle hoisted functions on pre-order traversal, so that they
// get hit before other things in the scope.
if (NodeUtil.isStatementParent(n)) {
for (Node child = n.getFirstChild();
child != null;
child = child.getNext()) {
if (NodeUtil.isHoistedFunctionDeclaration(child)) {
defineFunctionLiteral(child, n);
}
}
}
}
return descend;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
inputId = t.getInputId();
attachLiteralTypes(t, n);
switch (n.getType()) {
case Token.CALL:
checkForClassDefiningCalls(t, n, parent);
checkForCallingConventionDefiningCalls(n, delegateCallingConventions);
break;
case Token.FUNCTION:
if (t.getInput() == null || !t.getInput().isExtern()) {
nonExternFunctions.add(n);
}
// Hoisted functions are handled during pre-traversal.
if (!NodeUtil.isHoistedFunctionDeclaration(n)) {
defineFunctionLiteral(n, parent);
}
break;
case Token.ASSIGN:
// Handle initialization of properties.
Node firstChild = n.getFirstChild();
if (firstChild.isGetProp() &&
firstChild.isQualifiedName()) {
maybeDeclareQualifiedName(t, n.getJSDocInfo(),
firstChild, n, firstChild.getNext());
}
break;
case Token.CATCH:
defineCatch(n, parent);
break;
case Token.VAR:
defineVar(n, parent);
break;
case Token.GETPROP:
// Handle stubbed properties.
if (parent.isExprResult() &&
n.isQualifiedName()) {
maybeDeclareQualifiedName(t, n.getJSDocInfo(), n, parent, null);
}
break;
}
// Analyze any @lends object literals in this statement.
if (n
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>.getParent() != null && NodeUtil.isStatement(n) &&
lentObjectLiterals != null) {
for (Node objLit : lentObjectLiterals) {
defineObjectLiteral(objLit);
}
lentObjectLiterals.clear();
}
}
private void attachLiteralTypes(NodeTraversal t, Node n) {
switch (n.getType()) {
case Token.NULL:
n.setJSType(getNativeType(NULL_TYPE));
break;
case Token.VOID:
n.setJSType(getNativeType(VOID_TYPE));
break;
case Token.STRING:
n.setJSType(getNativeType(STRING_TYPE));
break;
case Token.NUMBER:
n.setJSType(getNativeType(NUMBER_TYPE));
break;
case Token.TRUE:
case Token.FALSE:
n.setJSType(getNativeType(BOOLEAN_TYPE));
break;
case Token.REGEXP:
n.setJSType(getNativeType(REGEXP_TYPE));
break;
case Token.OBJECTLIT:
JSDocInfo info = n.getJSDocInfo();
if (info != null &&
info.getLendsName() != null) {
if (lentObjectLiterals == null) {
lentObjectLiterals = Lists.newArrayList();
}
lentObjectLiterals.add(n);
} else {
defineObjectLiteral(n);
}
break;
// NOTE(nicksantos): If we ever support Array tuples,
// we will need to put ARRAYLIT here as well.
}
}
private void defineObjectLiteral(Node objectLit) {
// Handle the @lends annotation.
JSType type = null;
JSDocInfo info = objectLit.getJSDocInfo();
if (info != null &&
info.getLendsName() != null) {
String lendsName = info.getLendsName();
Var lendsVar = scope.getVar(lendsName);
if (lendsVar == null) {
compiler.report(
JSError.make(sourceName, objectLit, UNKNOWN_LENDS, lendsName));
} else {
type = lendsVar.getType();
if (type == null) {
type = typeRegistry.getNativeType(UNKNOWN_TYPE);
}
if (!type.isSubtype(typeRegistry.getNativeType(OBJECT_TYPE))) {
compiler.report(
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS>Lit, qualifiedName, keyType, inferred);
} else if (keyType != null) {
setDeferredType(keyNode, keyType);
}
if (keyType != null && objLitType != null && declareOnOwner) {
// Declare this property on its object literal.
boolean isExtern = keyNode.isFromExterns();
objLitType.defineDeclaredProperty(memberName, keyType, keyNode);
}
}
}
/**
* Returns the type specified in a JSDoc annotation near a GETPROP or NAME.
*
* Extracts type information from either the {@code @type} tag or from
* the {@code @return} and {@code @param} tags.
*/
private JSType getDeclaredTypeInAnnotation(String sourceName,
Node node, JSDocInfo info) {
JSType jsType = null;
Node objNode =
node.isGetProp() ? node.getFirstChild() :
NodeUtil.isObjectLitKey(node, node.getParent()) ? node.getParent() :
null;
if (info != null) {
if (info.hasType()) {
jsType = info.getType().evaluate(scope, typeRegistry);
} else if (FunctionTypeBuilder.isFunctionTypeDeclaration(info)) {
String fnName = node.getQualifiedName();
jsType = createFunctionTypeFromNodes(
null, fnName, info, node);
}
}
return jsType;
}
/**
* Asserts that it's OK to define this node's name.
* The node should have a source name and be of the specified type.
*/
void assertDefinitionNode(Node n, int type) {
Preconditions.checkState(sourceName != null);
Preconditions.checkState(n.getType() == type);
}
/**
* Defines a catch parameter.
*/
void defineCatch(Node n, Node parent) {
assertDefinitionNode(n, Token.CATCH);
Node catchName = n.getFirstChild();
defineSlot(catchName, n, null);
}
/**
* Defines a VAR initialization.
*/
void defineVar(Node n, Node parent) {
assertDefinitionNode(n, Token.VAR);
JSDocInfo info = n.getJSDocInfo();
if (n.hasMoreThanOneChild()) {
if (info != null) {
// multiple children
compiler.report(JSError.make(sourceName, n, MULTIPLE_VAR_DEF));
}
for (Node
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> name : n.children()) {
defineName(name, n, parent, name.getJSDocInfo());
}
} else {
Node name = n.getFirstChild();
defineName(name, n, parent,
(info != null) ? info : name.getJSDocInfo());
}
}
/**
* Defines a function literal.
*/
void defineFunctionLiteral(Node n, Node parent) {
assertDefinitionNode(n, Token.FUNCTION);
// Determine the name and JSDocInfo and l-value for the function.
// Any of these may be null.
Node lValue = NodeUtil.getBestLValue(n);
JSDocInfo info = NodeUtil.getBestJSDocInfo(n);
String functionName = NodeUtil.getBestLValueName(lValue);
FunctionType functionType =
createFunctionTypeFromNodes(n, functionName, info, lValue);
// Assigning the function type to the function node
setDeferredType(n, functionType);
// Declare this symbol in the current scope iff it's a function
// declaration. Otherwise, the declaration will happen in other
// code paths.
if (NodeUtil.isFunctionDeclaration(n)) {
defineSlot(n.getFirstChild(), n, functionType);
}
}
/**
* Defines a variable based on the {@link Token#NAME} node passed.
* @param name The {@link Token#NAME} node.
* @param var The parent of the {@code name} node, which must be a
* {@link Token#VAR} node.
* @param parent {@code var}'s parent.
* @param info the {@link JSDocInfo} information relating to this
* {@code name} node.
*/
private void defineName(Node name, Node var, Node parent, JSDocInfo info) {
Node value = name.getFirstChild();
// variable's type
JSType type = getDeclaredType(sourceName, info, name, value);
if (type == null) {
// The variable's type will be inferred.
type = name.isFromExterns() ?
getNativeType(UNKNOWN_TYPE) : null;
}
defineSlot(name, var, type);
}
/**
* If a variable is assigned a function literal in the global scope,
* make that a declared type (even if there's no doc info).
* There's only one exception to this rule:
* if the return type is
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> * Find the ObjectType associated with the given slot.
* @param slotName The name of the slot to find the type in.
* @return An object type, or null if this slot does not contain an object.
*/
private ObjectType getObjectSlot(String slotName) {
Var ownerVar = scope.getVar(slotName);
if (ownerVar != null) {
JSType ownerVarType = ownerVar.getType();
return ObjectType.cast(ownerVarType == null ?
null : ownerVarType.restrictByNotNullOrUndefined());
}
return null;
}
/**
* Resolve any stub declarations to unknown types if we could not
* find types for them during traversal.
*/
void resolveStubDeclarations() {
for (StubDeclaration stub : stubDeclarations) {
Node n = stub.node;
Node parent = n.getParent();
String qName = n.getQualifiedName();
String propName = n.getLastChild().getString();
String ownerName = stub.ownerName;
boolean isExtern = stub.isExtern;
if (scope.isDeclared(qName, false)) {
continue;
}
// If we see a stub property, make sure to register this property
// in the type registry.
ObjectType ownerType = getObjectSlot(ownerName);
ObjectType unknownType = typeRegistry.getNativeObjectType(UNKNOWN_TYPE);
defineSlot(n, parent, unknownType, true);
if (ownerType != null &&
(isExtern || ownerType.isFunctionPrototypeType())) {
// If this is a stub for a prototype, just declare it
// as an unknown type. These are seen often in externs.
ownerType.defineInferredProperty(
propName, unknownType, n);
} else {
typeRegistry.registerPropertyOnType(
propName, ownerType == null ? unknownType : ownerType);
}
}
}
/**
* Collects all declared properties in a function, and
* resolves them relative to the global scope.
*/
private final class CollectProperties
extends AbstractShallowStatementCallback {
private final ObjectType thisType;
CollectProperties(ObjectType thisType) {
this.thisType = thisType;
}
@Override
public void visit(NodeTraversal t, Node n, Node parent) {
if (n.isExprResult()) {
Node child = n.getFirstChild();
switch (child.getType()) {
case Token.ASSIGN:
maybeCollectMember(t, child.getFirstChild(), child,
child.getLastChild());
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> break;
case Token.GETPROP:
maybeCollectMember(t, child, child, null);
break;
}
}
}
private void maybeCollectMember(NodeTraversal t,
Node member, Node nodeWithJsDocInfo, @Nullable Node value) {
JSDocInfo info = nodeWithJsDocInfo.getJSDocInfo();
// Do nothing if there is no JSDoc type info, or
// if the node is not a member expression, or
// if the member expression is not of the form: this.someProperty.
if (info == null ||
!member.isGetProp() ||
!member.getFirstChild().isThis()) {
return;
}
member.getFirstChild().setJSType(thisType);
JSType jsType = getDeclaredType(t.getSourceName(), info, member, value);
Node name = member.getLastChild();
if (jsType != null &&
(name.isName() || name.isString())) {
thisType.defineDeclaredProperty(
name.getString(),
jsType,
member);
}
}
} // end CollectProperties
}
/**
* A stub declaration without any type information.
*/
private static final class StubDeclaration {
private final Node node;
private final boolean isExtern;
private final String ownerName;
private StubDeclaration(Node node, boolean isExtern, String ownerName) {
this.node = node;
this.isExtern = isExtern;
this.ownerName = ownerName;
}
}
/**
* A shallow traversal of the global scope to build up all classes,
* functions, and methods.
*/
private final class GlobalScopeBuilder extends AbstractScopeBuilder {
private GlobalScopeBuilder(Scope scope) {
super(scope);
}
/**
* Visit a node in the global scope, and add anything it declares to the
* global symbol table.
*
* @param t The current traversal.
* @param n The node being visited.
* @param parent The parent of n
*/
@Override public void visit(NodeTraversal t, Node n, Node parent) {
super.visit(t, n, parent);
switch (n.getType()) {
case Token.VAR:
// Handle typedefs.
if (n.hasOneChild()) {
checkForTypedef(t, n.getFirstChild(), n.getJSDocInfo());
}
break;
}
}
@Override
void maybeDeclareQualifiedName(
Node
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> ObjectType typeOfThis,
String templateTypeName, boolean isConstructor, boolean nativeType) {
super(registry, name,
registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE),
nativeType);
setPrettyPrint(true);
Preconditions.checkArgument(source == null ||
Token.FUNCTION == source.getType());
Preconditions.checkNotNull(arrowType);
this.source = source;
this.kind = isConstructor ? Kind.CONSTRUCTOR : Kind.ORDINARY;
if (isConstructor) {
this.typeOfThis = typeOfThis != null ?
typeOfThis : new InstanceObjectType(registry, this, nativeType);
} else {
this.typeOfThis = typeOfThis != null ?
typeOfThis :
registry.getNativeObjectType(JSTypeNative.UNKNOWN_TYPE);
}
this.call = arrowType;
this.templateTypeName = templateTypeName;
}
/** Creates an instance for a function that is an interface. */
private FunctionType(JSTypeRegistry registry, String name, Node source) {
super(registry, name,
registry.getNativeObjectType(JSTypeNative.FUNCTION_INSTANCE_TYPE));
setPrettyPrint(true);
Preconditions.checkArgument(source == null ||
Token.FUNCTION == source.getType());
Preconditions.checkArgument(name != null);
this.source = source;
this.call = new ArrowType(registry, new Node(Token.PARAM_LIST), null);
this.kind = Kind.INTERFACE;
this.typeOfThis = new InstanceObjectType(registry, this);
}
/** Creates an instance for a function that is an interface. */
static FunctionType forInterface(
JSTypeRegistry registry, String name, Node source) {
return new FunctionType(registry, name, source);
}
@Override
public boolean isInstanceType() {
// The universal constructor is its own instance, bizarrely.
return isEquivalentTo(registry.getNativeType(U2U_CONSTRUCTOR_TYPE));
}
@Override
public boolean isConstructor() {
return kind == Kind.CONSTRUCTOR;
}
@Override
public boolean isInterface() {
return kind == Kind.INTERFACE;
}
@Override
public boolean isOrdinaryFunction() {
return kind == Kind.ORDINARY;
}
@Override
public FunctionType toMaybeFunctionType() {
return this;
}
@Override
public boolean canBeCalled() {
return true;
}
public boolean hasImplementedInterfaces() {
if (!implementedInterfaces
Closure, 24
<FILEB>
<CHANGES>
if (parent.isVar() &&
n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
<CHANGES>
} else if (v.isBleedingFunction()) {
<CHANGEE>
<CHANGES>
} else if (parent.getType() == Token.LP) {
<CHANGEE>
<CHANGES>
<CHANGEE>
<FILEE>
<FILEB>
int endChar = next == null ? Integer.MAX_VALUE : next.getCharno();
SourcePosition<AliasTransformation> pos =
new SourcePosition<AliasTransformation>() {};
pos.setPositionInformation(
n.getLineno(), n.getCharno(), endLine, endChar);
return pos;
}
private void report(NodeTraversal t, Node n, DiagnosticType error,
String... arguments) {
compiler.report(t.makeError(n, error, arguments));
hasErrors = true;
}
private void findAliases(NodeTraversal t) {
Scope scope = t.getScope();
for (Var v : scope.getVarIterable()) {
Node n = v.getNode();
int type = n.getType();
Node parent = n.getParent();
<CHANGES>
if (parent.isVar()) {
if (n.hasChildren() && n.getFirstChild().isQualifiedName()) {
<CHANGEE>
String name = n.getString();
Var aliasVar = scope.getVar(name);
aliases.put(name, aliasVar);
String qualifiedName =
aliasVar.getInitialValue().getQualifiedName();
transformation.addAlias(name, qualifiedName);
<CHANGES>
<CHANGEE>
// Bleeding functions already get a BAD_PARAMETERS error, so just
// do nothing.
<CHANGES>
<CHANGEE>
// Parameters of the scope function also get a BAD_PARAMETERS
// error.
} else {
// TODO(robbyw): Support using locals for private variables.
report(t, n, GOOG_SCOPE_NON_ALIAS_LOCAL, n.getString());
<CHANGES>
}
<CHANGEE>
}
}
}
private void validateScopeCall(NodeTraversal t, Node n, Node parent) {
if (preprocessorSymbolTable != null) {
preprocessorSymbolTable.addReference(n.getFirstChild());
}
if (!parent.isExprResult()) {
report(t, n, GOOG_SCOPE_USED_IMPROPERLY);
}
if (n.getChildCount() != 2) {
// The goog.scope call should have exactly 1 parameter. The first
<FILEE>
<SCANS> "apply" function lazily.
FunctionParamBuilder builder = new FunctionParamBuilder(registry);
// ECMA-262 says that apply's second argument must be an Array
// or an arguments object. We don't model the arguments object,
// so let's just be forgiving for now.
// TODO(nicksantos): Model the Arguments object.
builder.addOptionalParams(
registry.createNullableType(getTypeOfThis()),
registry.createNullableType(
registry.getNativeType(JSTypeNative.OBJECT_TYPE)));
defineDeclaredProperty(name,
new FunctionBuilder(registry)
.withParams(builder)
.withReturnType(getReturnType())
.build(),
source);
}
}
return super.getPropertyType(name);
}
/**
* Get the return value of calling "bind" on this function
* with the specified number of arguments.
*
* If -1 is passed, then we will return a result that accepts
* any parameters.
*/
public FunctionType getBindReturnType(int argsToBind) {
FunctionBuilder builder = new FunctionBuilder(registry)
.withReturnType(getReturnType());
if (argsToBind >= 0) {
Node origParams = getParametersNode();
if (origParams != null) {
Node params = origParams.cloneTree();
for (int i = 1; i < argsToBind && params.getFirstChild() != null; i++) {
if (params.getFirstChild().isVarArgs()) {
break;
}
params.removeFirstChild();
}
builder.withParamsNode(params);
}
}
return builder.build();
}
/**
* Notice that "call" and "bind" have the same argument signature,
* except that all the arguments of "bind" (except the first)
* are optional.
*/
private FunctionType getCallOrBindSignature(boolean isCall) {
boolean isBind = !isCall;
FunctionBuilder builder = new FunctionBuilder(registry)
.withReturnType(isCall ? getReturnType() : getBindReturnType(-1));
Node origParams = getParametersNode();
if (origParams != null) {
Node params = origParams.cloneTree();
Node thisTypeNode = Node.newString(Token.NAME, "thisType");
thisTypeNode.setJSType(
registry.createOptionalNullableType(getTypeOfThis()));
params.addChildToFront(thisTypeNode);
thisTypeNode.setOptionalArg(isCall